AI Integration Quick Reference
AI Integration Quick Reference
| Field | Value |
|---|---|
| Package | @cometchat/chat-uikit-react |
| Key class | CometChatTextFormatter (abstract base class for custom formatters) |
| Required setup | CometChatUIKit.init(UIKitSettings) then CometChatUIKit.login("UID") |
| Purpose | Extend to create custom inline text patterns with regex, styling, and callbacks |
| Features | Text formatting, customizable styles, dynamic text replacement, input field integration, key event callbacks |
| Sample app | GitHub |
| Related | ShortCut Formatter | Mentions Formatter | All Guides |
CometChatTextFormatter is an abstract class for formatting text in the message composer and message bubbles. Extend it to build custom formatters — hashtags, keywords, or any regex-based pattern.
| Capability | Description |
|---|---|
| Text formatting | Auto-format text based on regex patterns and styles |
| Custom styles | Set colors, fonts, and backgrounds for matched text |
| Dynamic replacement | Regex-based find-and-replace in user input |
| Input integration | Real-time monitoring of the composer input field |
| Key event callbacks | Hooks for keyUp and keyDown events |
Always wrap formatted output in a
<span> with a unique CSS class (e.g. "custom-hashtag"). This tells the UI Kit to render it as-is instead of sanitizing it.Steps
1. Import the base class
import { CometChatTextFormatter } from "@cometchat/chat-uikit-react";
2. Extend it
class HashTagTextFormatter extends CometChatTextFormatter {
...
}
3. Configure tracking character and regex
Set the character that triggers formatting, the regex to match, and the regex to strip formatting back to plain text.this.setTrackingCharacter("#");
this.setRegexPatterns([/\B#(\w+)\b/g]);
this.setRegexToReplaceFormatting([
/<span class="custom-hashtag" style="color: #30b3ff;">#(\w+)<\/span>/g,
]);
4. Set key event callbacks
this.setKeyUpCallBack(this.onKeyUp.bind(this));
this.setKeyDownCallBack(this.onKeyDown.bind(this));
5. Implement formatting methods
getFormattedText(inputText:string) { ... }
getOriginalText(inputText:string) { ... }
customLogicToFormatText(inputText: string) { ... }
Example
A hashtag formatter used with CometChatMessageList and CometChatMessageComposer.
- HashTagTextFormatter.ts
- MessageListDemo.tsx
import { CometChatTextFormatter } from "@cometchat/chat-uikit-react";
class HashTagTextFormatter extends CometChatTextFormatter {
constructor() {
super();
this.setTrackingCharacter("#");
this.setRegexPatterns([/\B#(\w+)\b/g]);
this.setRegexToReplaceFormatting([/#(\w+)/g]);
this.setKeyUpCallBack(this.onKeyUp.bind(this));
this.setKeyDownCallBack(this.onKeyDown.bind(this));
this.setReRender(() => {
console.log("Re-rendering message composer to update text content.");
});
this.initializeComposerTracking();
}
initializeComposerTracking() {
const composerInput = document.getElementById("yourComposerInputId");
this.setInputElementReference(composerInput);
}
getCaretPosition(): number {
if (!this.inputElementReference) return 0;
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) return 0;
const range = selection.getRangeAt(0);
const clonedRange = range.cloneRange();
clonedRange.selectNodeContents(this.inputElementReference);
clonedRange.setEnd(range.endContainer, range.endOffset);
return clonedRange.toString().length;
}
setCaretPosition(position: number) {
if (!this.inputElementReference) return;
const range = document.createRange();
const selection = window.getSelection();
if (!selection) return;
range.setStart(
this.inputElementReference.childNodes[0] || this.inputElementReference,
position
);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
}
onKeyUp(event: KeyboardEvent) {
if (event.key === this.trackCharacter) {
this.startTracking = true;
}
if (this.startTracking && (event.key === " " || event.key === "Enter")) {
const caretPosition = this.getCaretPosition();
this.formatText();
this.setCaretPosition(caretPosition);
}
if (
this.startTracking &&
event.key !== " " &&
event.key !== "Enter" &&
this.getCaretPosition() === this.inputElementReference?.innerText?.length
) {
this.startTracking = false;
}
}
formatText() {
const inputValue =
this.inputElementReference?.innerText ||
this.inputElementReference?.textContent ||
"";
const formattedText = this.getFormattedText(inputValue);
if (this.inputElementReference) {
this.inputElementReference.innerHTML = formattedText || "";
this.reRender();
}
}
onKeyDown(event: KeyboardEvent) {}
getFormattedText(inputText: string) {
if (!inputText) return;
return this.customLogicToFormatText(inputText);
}
customLogicToFormatText(inputText: string) {
return inputText.replace(
/\B#(\w+)\b/g,
'<span class="custom-hashtag" style="color: #5dff05;">#$1</span>'
);
}
getOriginalText(inputText: string) {
if (!inputText) return "";
for (let i = 0; i < this.regexToReplaceFormatting.length; i++) {
let regexPattern = this.regexToReplaceFormatting[i];
if (inputText) {
inputText = inputText.replace(regexPattern, "#$1");
}
}
return inputText;
}
}
export default HashTagTextFormatter;
Pass the formatter via the
textFormatters prop.import { HashTagTextFormatter } from "./HashTagTextFormatter";
export default function MessageListDemo() {
const [chatUser, setChatUser] = React.useState<CometChat.User | undefined>();
React.useEffect(() => {
CometChat.getUser("uid").then((user) => {
setChatUser(user);
})
}, [])
return (
<CometChatMessageList
user={chatUser}
textFormatters={[new HashTagTextFormatter()]}
/>
);
}
Methods Reference
| Field | Setter | Description |
|---|---|---|
trackCharacter | setTrackingCharacter(char) | Character that starts tracking (e.g. # for hashtags) |
currentCaretPosition | setCaretPositionAndRange(selection, range) | Current selection set by the composer |
currentRange | setCaretPositionAndRange(selection, range) | Text range or cursor position set by the composer |
inputElementReference | setInputElementReference(element) | DOM reference to the composer input field |
regexPatterns | setRegexPatterns(patterns) | Regex patterns to match text for formatting |
regexToReplaceFormatting | setRegexToReplaceFormatting(patterns) | Regex patterns to strip formatting back to plain text |
keyUpCallBack | setKeyUpCallBack(fn) | Callback for key up events |
keyDownCallBack | setKeyDownCallBack(fn) | Callback for key down events |
reRender | setReRender(fn) | Triggers a re-render of the composer to update displayed text |
loggedInUser | setLoggedInUser(user) | Logged-in user object, set by composer and text bubbles |
id | setId(id) | Unique identifier for the formatter instance |
Don’t modify
textContent or innerHTML of the input element directly. Call reRender instead — the composer will invoke getFormattedText for all formatters in order.Override Methods
- getFormattedText
- onKeyUp
- onKeyDown
- registerEventListeners
- getOriginalText
Returns formatted HTML from input text, or edits at cursor position if
inputText is null.getFormattedText(inputText: string | null, params: any): string | void {
if (!inputText) {
return; // edit at cursor position
}
return this.customLogicToFromatText(inputText);
}
Handles
keyup events. Start tracking when the track character is typed.onKeyUp(event: KeyboardEvent) {
if (event.key == this.trackCharacter) {
this.startTracking = true;
}
if (this.startTracking && event.key == " ") {
this.debouncedFormatTextOnKeyUp();
}
}
Handles
keydown events.onKeyDown(event: KeyboardEvent) {}
Called by the composer and text bubbles for each HTML element in the formatted string. Check for your formatter’s CSS class before attaching listeners.
registerEventListeners(element: HTMLElement, domTokenList: DOMTokenList) {
let classList: string[] = Array.from(domTokenList);
for (let i = 0; i < classList.length; i++) {
if (classList[i] in this.mentionsCssClassMapping) {
element.addEventListener("click", (event: Event) => {
clearTimeout(this.timeoutID);
CometChatUIEvents.ccMouseEvent.next({
body: {
CometChatUserGroupMembersObject:
this.mentionsCssClassMapping[classList[i]],
message: this.messageObject ?? null,
id: this.getId(),
},
event,
source: MouseEventSource.mentions,
});
});
element.addEventListener("mouseover", (event: Event) => {
this.timeoutID = setTimeout(() => {
this.mouseOverEventDispatched = true;
CometChatUIEvents.ccMouseEvent.next({
body: {
CometChatUserGroupMembersObject:
this.mentionsCssClassMapping[classList[i]],
message: this.messageObject ?? null,
id: this.getId(),
},
event,
source: MouseEventSource.mentions,
});
}, CometChatUtilityConstants.MentionsFormatter.MENTIONS_HOVER_TIMEOUT);
});
element.addEventListener("mouseout", (event: Event) => {
clearTimeout(this.timeoutID);
if (this.mouseOverEventDispatched) {
CometChatUIEvents.ccMouseEvent.next({
body: {
CometChatUserGroupMembersObject:
this.mentionsCssClassMapping[classList[i]],
message: this.messageObject ?? null,
id: this.getId(),
},
event,
source: MouseEventSource.mentions,
});
this.mouseOverEventDispatched = false;
}
});
}
}
return element;
}
Strips formatting and returns plain text.
getOriginalText(inputText: string | null | undefined): string {
if (!inputText) return "";
for (let i = 0; i < this.regexToReplaceFormatting.length; i++) {
let regexPattern = this.regexToReplaceFormatting[i];
if (inputText) {
inputText = inputText.replace(regexPattern, "$1");
}
}
return inputText;
}
Next Steps
Mentions Formatter
Add @mentions with styled tokens.
Message Composer
Customize the message input component.
All Guides
Browse all feature and formatter guides.
Sample App
Full working sample application on GitHub.