Skip to main content

Documentation Index

Fetch the complete documentation index at: https://cometchat-22654f5b-release-flutter-v5-stable.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Rich Text Formatting Guide

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:
<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:
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:
// 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:
FormatWindows/LinuxMac
BoldCtrl+BCmd+B
ItalicCtrl+ICmd+I
UnderlineCtrl+UCmd+U
UndoCtrl+ZCmd+Z
RedoCtrl+Y or Ctrl+Shift+ZCmd+Shift+Z

Block Formatting

Apply formatting to entire blocks:
// Code block
this.editorService.toggleCodeBlock(this.editor);

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

Lists

Creating Lists

// 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

// 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)
addLink() {
  const url = prompt('Enter URL:');
  if (url) {
    this.editorService.setLink(this.editor, url);
  }
}
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

// 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:
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

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:
/* 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

// 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:
loadMessage(message: CometChat.TextMessage) {
  this.editorService.setContentWithMentions(
    this.editor,
    message.getText(),
    message.getMentionedUsers()
  );
}

Undo/Redo

Using History

// 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

// 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

// 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

// 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

// 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:
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

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:
// 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:
: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:
.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

ngOnDestroy() {
  if (this.editor) {
    this.editorService.destroyEditor(this.editor);
  }
}

2. Use Reactive Signals

// 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

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

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

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

Formatting Not Working

// 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

// 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