Skip to main content
Text formatters detect patterns in message text and transform them into formatted HTML for display in bubbles. They run as a pipeline — each formatter receives the output of the previous one, sorted by priority (lower number = runs first). The Text plugin provides three built-in formatters, and you can add your own.

Built-in Formatters

The Formatter Interface

All formatters extend the abstract CometChatTextFormatter class. id is its only abstract member — everything else has a working default, so the smallest useful formatter is an id plus one transform method. Porting from v6? Read this page for the class, then Porting a v6 formatter for the members v6 had that v7 does not.

Where your formatter runs

A formatter can take part in two independent pipelines. Display is the common case; live input is opt-in. Formatters run in priority order (lower first), each receiving the previous one’s output.
The built-ins run alongside yours. Markdown (10), mentions (20) and URL (100) are always in the pipeline, so a formatter that matches what a built-in already produced will process it twice. A ported v6 URL formatter is the common case: both it and CometChatUrlFormatter match the same URL, and the second one rewrites the first one’s <a> into broken markup. Drop your own URL handling in favour of the built-in, or give your formatter a pattern and priority that keep it clear of the built-ins’ output.
Errors inside the display pipeline are caught, and your formatter is dropped. Text bubbles log console.warn("CometChatTextBubble: Error applying text formatter") and skip it; conversation subtitles, reply and edit previews, saved messages and search results swallow the error silently. A formatter that renders nothing is usually a formatter that threw — check the console before assuming it was never registered.

Display members

The display pipeline never receives the message object or the logged-in user. A formatter sees text only.

Live-input members

Only relevant if your formatter drives the composer’s editable input. Display-only formatters ignore this section.
Live input requires enableRichTextEditor on the composer. The composer binds formatters to its editor — assigning inputElementReference, calling initializeComposerTracking() and fanning keystrokes — only when the rich-text editor is active. Without that prop, inputElementReference stays null, onKeyUp()/onKeyDown() are never called and formatText() does nothing, with no error. Display formatting is unaffected and works either way.
v7 does not reformat the live input on keystrokes by itself. The base onKeyUp() only forwards to your callback, and formatText() runs only on entering rich-text edit mode. To reformat as the user types, call formatText() from your own onKeyUp() override or from the callback you register.

The full class

Every member of the class, annotated with who calls it and what the default does. “Called by the kit” means the UI Kit invokes it on your formatter, so overriding it takes part in the pipeline; “called by you” means nothing invokes it for you.

Ids must be unique

The kit merges formatters into a map keyed by id. Two formatters with the same id leave only the last one, and reusing a built-in id — markdown-formatter, mentions-formatter, url-formatter — replaces that built-in. Plain JavaScript will run a formatter with no id, but every id-less formatter collapses onto the same key, so always declare one.

Creating a Custom Formatter

Here’s a hashtag formatter that wraps #word patterns in a styled span:
src/formatters/HashtagFormatter.ts

Registering Custom Formatters

A custom formatter is not registered on its own — it must be wrapped in a custom text plugin (see Plugins overview) and that plugin registered via the provider’s plugins prop (see With Additional Plugins). Custom formatters are registered by creating a custom text plugin that provides them:
src/plugins/CustomTextPlugin.ts
Then pass it in your provider’s plugins prop:
Since user plugins are prepended before the defaults in the registry, your custom text plugin takes precedence over the built-in one (first match wins).

Formatter Details

CometChatMarkdownFormatter

Converts markdown syntax to HTML. Runs first (priority 10) so subsequent formatters operate on HTML output.
  • **bold** → <b>bold</b>
  • _italic_ → <i>italic</i>
  • __underline__ or ++underline++ → <u>underline</u>
  • ~~strikethrough~~ → <s>strikethrough</s>
  • `inline code` → <code>inline code</code>
  • ```code block``` → <pre><code>code block</code></pre>
  • > blockquote → <blockquote>blockquote</blockquote>
  • [text](url) → <a href="url">text</a>
  • 1. item → ordered list; • item / - item → unordered list

CometChatMentionsFormatter

Resolves SDK mention tokens (<@uid:xxx> and <@all:label>) into styled mention chips. Requires the mentioned-users list from the message to resolve UIDs to display names.

CometChatUrlFormatter

Detects bare URLs (https://... and www.) and wraps them in clickable <a> tags with target="_blank" and rel="noopener noreferrer". Protects existing markdown links and <a> tags from double-processing.

Tips

  • Priority matters — markdown must run before mentions/URLs so it doesn’t break HTML tags
  • Protect code blocks — formatters should skip content inside <code> and <pre> tags
  • Keep it fast — formatters run on every text message render; avoid expensive operations
  • Use shouldFormat() — override to skip formatting for specific messages
  • Store metadata — use this.metadata to expose extracted data (URLs, hashtags, mentions) to consumers