> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-audit-mechanical-fixes.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Rich Text Formatting Guide

> Use rich text formatting in the CometChat Angular v5 UIKit — text styling, mentions, and links via a lightweight rich text editor built on native browser APIs.

This guide explains how to use rich text formatting in the CometChat Angular V5 UIKit, including text formatting, mentions, links, and more.

## Overview

The UIKit provides a custom rich text editor built on native browser APIs, offering:

* Lightweight implementation (no external dependencies)
* Full formatting support (bold, italic, underline, etc.)
* Mentions integration
* Undo/redo history
* Keyboard shortcuts
* Full accessibility

## Basic Usage

### In MessageComposer

The `CometChatMessageComposer` component includes rich text formatting by default:

```html theme={null}
<cometchat-message-composer
  [user]="user"
  [placeholder]="'Type your message...'"
  (sendMessage)="onSendMessage($event)">
</cometchat-message-composer>
```

### Custom Implementation

To use the rich text editor in your own components:

```typescript expandable theme={null}
import { Component, OnInit, OnDestroy, inject } from '@angular/core';
import { RichTextEditorService } from '@cometchat/chat-uikit-angular';

@Component({
  selector: 'app-custom-editor',
  template: `
    <div class="editor-container">
      <div #editorElement class="editor"></div>
      <div class="toolbar">
        <button (click)="toggleBold()">Bold</button>
        <button (click)="toggleItalic()">Italic</button>
        <button (click)="toggleUnderline()">Underline</button>
      </div>
    </div>
  `
})
export class CustomEditorComponent implements OnInit, OnDestroy {
  private editorService = inject(RichTextEditorService);
  private editor: any;
  
  @ViewChild('editorElement') editorElement!: ElementRef;
  
  ngOnInit() {
    this.editor = this.editorService.createEditor({
      placeholder: 'Type something...',
      autofocus: true,
      onUpdate: (html, text) => {
        console.log('Content:', html, text);
      }
    }, this.editorElement.nativeElement);
  }
  
  ngOnDestroy() {
    this.editorService.destroyEditor(this.editor);
  }
  
  toggleBold() {
    this.editorService.toggleBold(this.editor);
  }
  
  toggleItalic() {
    this.editorService.toggleItalic(this.editor);
  }
  
  toggleUnderline() {
    this.editorService.toggleUnderline(this.editor);
  }
}
```

## Text Formatting

### Inline Formatting

Apply formatting to selected text or at cursor position:

```typescript expandable theme={null}
// Bold
this.editorService.toggleBold(this.editor);

// Italic
this.editorService.toggleItalic(this.editor);

// Underline
this.editorService.toggleUnderline(this.editor);

// Strikethrough
this.editorService.toggleStrikethrough(this.editor);

// Inline code
this.editorService.toggleCode(this.editor);
```

### Keyboard Shortcuts

Users can apply formatting using keyboard shortcuts:

| Format    | Windows/Linux              | Mac           |
| --------- | -------------------------- | ------------- |
| Bold      | `Ctrl+B`                   | `Cmd+B`       |
| Italic    | `Ctrl+I`                   | `Cmd+I`       |
| Underline | `Ctrl+U`                   | `Cmd+U`       |
| Undo      | `Ctrl+Z`                   | `Cmd+Z`       |
| Redo      | `Ctrl+Y` or `Ctrl+Shift+Z` | `Cmd+Shift+Z` |

### Block Formatting

Apply formatting to entire blocks:

```typescript theme={null}
// Code block
this.editorService.toggleCodeBlock(this.editor);

// Blockquote
this.editorService.toggleBlockquote(this.editor);
```

## Lists

### Creating Lists

```typescript theme={null}
// Ordered (numbered) list
this.editorService.toggleOrderedList(this.editor);

// Unordered (bullet) list
this.editorService.toggleBulletList(this.editor);
```

### List Behavior

* **Enter**: Creates a new list item
* **Enter** (twice): Exits the list
* **Tab**: Indents the list item
* **Shift+Tab**: Outdents the list item

### Example

```typescript theme={null}
// Create a numbered list
this.editorService.toggleOrderedList(this.editor);

// User types:
// 1. First item [Enter]
// 2. Second item [Enter]
// 3. Third item [Enter][Enter]
// (exits list)
```

## Links

### Adding Links

```typescript theme={null}
addLink() {
  const url = prompt('Enter URL:');
  if (url) {
    this.editorService.setLink(this.editor, url);
  }
}
```

### Removing Links

```typescript theme={null}
removeLink() {
  this.editorService.setLink(this.editor, null);
}
```

### URL Validation

The editor automatically validates URLs:

* Invalid URLs display an error
* URLs without protocol are prefixed with `https://`
* Pasted URLs are automatically converted to clickable links

### Example

```typescript theme={null}
// Valid URLs
this.editorService.setLink(this.editor, 'https://example.com');
this.editorService.setLink(this.editor, 'example.com'); // Auto-prefixed

// Invalid URL
this.editorService.setLink(this.editor, 'not a url'); // Shows error
```

## Mentions

### Basic Mention Integration

The editor integrates with `CometChatMentionsFormatter` for @mention functionality:

```typescript expandable theme={null}
ngOnInit() {
  this.editor = this.editorService.createEditor({
    placeholder: 'Type @ to mention someone...',
    onMentionStart: (query) => {
      this.showMentionPopup(query);
    },
    onMentionEnd: () => {
      this.hideMentionPopup();
    },
    getMentionSuggestions: async (query) => {
      return await this.searchUsers(query);
    },
    onMentionSelect: (item) => {
      this.insertMention(item);
    }
  });
}
```

### Inserting Mentions

```typescript expandable theme={null}
insertMention(user: CometChat.User) {
  const isSelf = user.getUid() === this.loggedInUser.getUid();
  
  this.editorService.insertMention(
    this.editor,
    user.getUid(),
    user.getName(),
    this.queryLength + 1, // +1 for @ symbol
    isSelf
  );
}
```

### Mention Styling

Mentions are styled differently based on whether they're for the current user:

```css expandable theme={null}
/* Self mention (logged-in user) */
.cometchat-mention--self {
  background-color: var(--cometchat-primary-color);
  color: var(--cometchat-static-white);
}

/* Other user mention */
.cometchat-mention--other {
  background-color: var(--cometchat-background-color-03);
  color: var(--cometchat-text-color-primary);
}
```

### Mention Limits

* Maximum of **10 unique mentions** per message
* Attempting to add more displays an error

### Getting Mention Data

```typescript theme={null}
// Get text with CometChat mention format
const formattedText = this.editorService.getTextWithMentionFormat(this.editor);
// Output: "Hello <@uid:user123>, how are you?"

// Get unique mention UIDs
const mentionUids = this.editorService.getUniqueMentionUids(this.editor);
console.log('Mentioned users:', Array.from(mentionUids));
```

### Loading Messages with Mentions

When loading a message that contains mentions:

```typescript theme={null}
loadMessage(message: CometChat.TextMessage) {
  this.editorService.setContentWithMentions(
    this.editor,
    message.getText(),
    message.getMentionedUsers()
  );
}
```

## Undo/Redo

### Using History

```typescript theme={null}
// Undo
if (this.editorService.canUndo(this.editor)) {
  this.editorService.undo(this.editor);
}

// Redo
if (this.editorService.canRedo(this.editor)) {
  this.editorService.redo(this.editor);
}
```

### History Grouping

Rapid typing is automatically grouped into single undo steps:

* Typing delay: 500ms
* Each formatting operation creates a new undo step
* History stack size: 100 entries

### Example

```typescript theme={null}
// User types "Hello world" quickly
// This creates ONE undo step

// User applies bold
// This creates a SECOND undo step

// Pressing Ctrl+Z undoes the bold
// Pressing Ctrl+Z again removes "Hello world"
```

## Content Management

### Getting Content

```typescript theme={null}
// Get HTML with formatting
const html = this.editorService.getHTML(this.editor);

// Get plain text without formatting
const text = this.editorService.getText(this.editor);

// Get metadata
const metadata = this.editorService.getRichTextMetadata(this.editor);
console.log('Has formatting:', metadata.hasFormatting);
```

### Setting Content

```typescript theme={null}
// Set HTML content
this.editorService.setContent(this.editor, '<p><strong>Bold text</strong></p>');

// Clear content
this.editorService.clearContent(this.editor);

// Insert text at cursor
this.editorService.insertText(this.editor, 'Hello');
```

### Checking Content State

```typescript theme={null}
// Check if empty
if (this.editorService.isEmpty(this.editor)) {
  console.log('Editor is empty');
}

// Check if has formatting
if (this.editorService.hasFormatting(this.editor)) {
  console.log('Content has rich text formatting');
}
```

## Format State

### Tracking Format State

Use the reactive signal to track which formats are active:

```typescript theme={null}
formatState = this.editorService.formatState;

// In template
<button [class.active]="formatState().bold">Bold</button>
<button [class.active]="formatState().italic">Italic</button>
<button [class.active]="formatState().link">Link</button>
```

### Manual Format State Check

```typescript theme={null}
const formatState = this.editorService.getFormatState(this.editor);

if (formatState.bold) {
  console.log('Bold is active');
}

if (formatState.orderedList) {
  console.log('In ordered list');
}
```

## Copy, Paste, and Drag

### Paste Handling

The editor automatically handles pasted content:

```typescript expandable theme={null}
// Formatted text paste
// - Preserves compatible formatting (bold, italic, etc.)
// - Strips unsupported formatting
// - Sanitizes for XSS protection

// Plain text paste
// - Inserts without formatting

// URL paste
// - Automatically converts to clickable link
```

### Drag and Drop

Users can drag and drop text within the editor:

* Formatting is preserved
* Cursor position is updated

### Copy

When users copy formatted text:

* Formatting is preserved in clipboard
* Both HTML and plain text formats are available

## Accessibility

### Keyboard Navigation

All features are accessible via keyboard:

* **Tab**: Focus editor
* **Arrow keys**: Navigate content
* **Escape**: Close mention popup
* **Enter**: New line or list item
* **Formatting shortcuts**: See table above

### Screen Reader Support

The editor announces:

* Formatting changes ("Bold applied")
* Mention insertions ("Mentioned John Doe")
* Undo/redo operations ("Undone", "Redone")

### ARIA Attributes

The editor includes proper ARIA attributes:

* `role="textbox"`
* `aria-label="Message editor"`
* `aria-multiline="true"`
* `aria-placeholder="Type your message..."`

## Styling

### Using CSS Variables

Customize the editor appearance using CSS variables:

```css expandable theme={null}
:root {
  /* Editor background */
  --cometchat-background-color-01: #ffffff;
  
  /* Text color */
  --cometchat-text-color-primary: #141414;
  
  /* Placeholder color */
  --cometchat-text-color-secondary: #666666;
  
  /* Focus border */
  --cometchat-primary-color: #6852D6;
  
  /* Mention colors */
  --cometchat-primary-color: #6852D6;
  --cometchat-background-color-03: #f0f0f0;
}
```

### Custom Styles

Apply custom styles to the editor:

```css expandable theme={null}
.cometchat-rich-text-editor {
  min-height: 100px;
  max-height: 300px;
  overflow-y: auto;
  padding: var(--cometchat-spacing-3);
  border: 1px solid var(--cometchat-border-color-light);
  border-radius: var(--cometchat-radius-2);
}

.cometchat-rich-text-editor:focus {
  outline: none;
  border-color: var(--cometchat-primary-color);
}
```

## Performance

### Optimization Tips

1. **Debounce updates**: Use debouncing for expensive operations
2. **Lazy load mentions**: Load mention suggestions on demand
3. **Limit history**: History is automatically limited to 100 entries
4. **Clean up**: Always destroy editors when done

### Performance Metrics

The editor is optimized for:

* **Typing latency**: \< 50ms per keystroke (10,000 characters)
* **Formatting operations**: \< 100ms
* **Initialization**: \< 50ms
* **Paste operations**: \< 200ms

## Security

### XSS Protection

All HTML is sanitized to prevent XSS attacks:

* Script tags removed
* Event handlers removed
* Dangerous attributes removed
* Only safe HTML tags allowed

### Content Validation

* URLs are validated before insertion
* Mention data is sanitized
* Pasted content is sanitized

## Best Practices

### 1. Always Clean Up

```typescript theme={null}
ngOnDestroy() {
  if (this.editor) {
    this.editorService.destroyEditor(this.editor);
  }
}
```

### 2. Use Reactive Signals

```typescript theme={null}
// Good: Use reactive signal
formatState = this.editorService.formatState;

// Avoid: Polling format state
setInterval(() => {
  this.formatState = this.editorService.getFormatState(this.editor);
}, 100);
```

### 3. Handle Empty State

```typescript theme={null}
sendMessage() {
  if (this.editorService.isEmpty(this.editor)) {
    // Show error: "Message cannot be empty"
    return;
  }
  
  const text = this.editorService.getTextWithMentionFormat(this.editor);
  // Send message...
}
```

### 4. Validate Before Sending

```typescript expandable theme={null}
sendMessage() {
  const text = this.editorService.getText(this.editor);
  
  if (text.trim().length === 0) {
    return; // Empty message
  }
  
  if (text.length > 10000) {
    // Show error: "Message too long"
    return;
  }
  
  // Send message...
}
```

## Troubleshooting

### Editor Not Focusing

```typescript theme={null}
// Ensure element is mounted before creating editor
ngAfterViewInit() {
  this.editor = this.editorService.createEditor(
    { autofocus: true },
    this.editorElement.nativeElement
  );
}
```

### Formatting Not Working

```typescript expandable theme={null}
// Check if editor is destroyed
if (this.editor.isDestroyed()) {
  console.error('Editor is destroyed');
  return;
}

// Check if editor is editable
if (!this.editor.isEditable()) {
  console.error('Editor is not editable');
  return;
}
```

### Mentions Not Showing

```typescript expandable theme={null}
// Ensure mention callbacks are configured
this.editor = this.editorService.createEditor({
  onMentionStart: (query) => {
    console.log('Mention started:', query);
    this.showMentionPopup(query);
  },
  getMentionSuggestions: async (query) => {
    return await this.searchUsers(query);
  }
});
```

## See Also

* [RichTextEditorService API Reference](/ui-kit/angular/api-reference/rich-text-editor-service)
* [MessageComposer Component](/ui-kit/angular/components/cometchat-message-composer)
