> ## Documentation Index
> Fetch the complete documentation index at: https://www.cometchat.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# ShortCut Formatter

> Implement !shortcut style text expansions with extension APIs or dialogs in CometChat React UI Kit.

<Accordion title="AI Integration Quick Reference">
  | Field           | Value                                                                                                            |
  | --------------- | ---------------------------------------------------------------------------------------------------------------- |
  | Package         | `@cometchat/chat-uikit-react`                                                                                    |
  | Key class       | `ShortcutFormatter` (extends `CometChatTextFormatter`)                                                           |
  | Required setup  | `CometChatUIKit.init(UIKitSettings)` then `CometChatUIKit.login("UID")`                                          |
  | Track character | `!` — triggers shortcut expansion in the message composer                                                        |
  | Sample app      | [GitHub](https://github.com/cometchat/cometchat-uikit-react/tree/v6/sample-app)                                  |
  | Related         | [Custom Text Formatter](/ui-kit/react/custom-text-formatter-guide) \| [All Guides](/ui-kit/react/guide-overview) |
</Accordion>

`ShortCutFormatter` extends [CometChatTextFormatter](/ui-kit/react/custom-text-formatter-guide) to expand shortcodes (like `!hb`) into full text via the Message Shortcuts extension. When a user types a shortcut, a dialog appears with the expansion — clicking it inserts the text.

<Frame>
  <img src="https://mintcdn.com/cometchat-22654f5b/2U5tVIzH12dbbFtr/images/af50b10c-shortcutformatter_overview_web_screens-4e5d3b6b65e42d8ce15bbb0f83605bfe.png?fit=max&auto=format&n=2U5tVIzH12dbbFtr&q=85&s=fad68904864fc108a5860263ba4e6bc2" width="1282" height="802" data-path="images/af50b10c-shortcutformatter_overview_web_screens-4e5d3b6b65e42d8ce15bbb0f83605bfe.png" />
</Frame>

***

## Steps

### 1. Import the base class

```javascript lines theme={null}
import { CometChatTextFormatter } from "@cometchat/chat-uikit-react";
```

### 2. Extend it

```javascript lines theme={null}
class ShortCutFormatter extends CometChatTextFormatter {
  ...
}
```

### 3. Set the track character

```javascript lines theme={null}
this.setTrackingCharacter("!");
```

### 4. Handle key events

Detect shortcuts on `keyDown` and trigger expansion logic.

```javascript lines theme={null}
onKeyDown(event: KeyboardEvent) {
  // Your implementation
}
```

### 5. Add dialog and formatting methods

```typescript lines theme={null}
openDialog(buttonText: string) { ... }
closeDialog() { ... }
handleButtonClick = () => { ... };
getFormattedText(text: string): string { return text; }
private getTextBeforeCaret(caretPosition: number): string { ... }
```

***

## Example

<Frame>
  <img src="https://mintcdn.com/cometchat-22654f5b/bUk-WwSNpBXF92Yc/images/dd3e5fe4-shortcutformatter-1ea213ac1d135f72a85bb4dc4dabc50f.png?fit=max&auto=format&n=bUk-WwSNpBXF92Yc&q=85&s=51d6340e972cd4cfb8d1ac0d64797463" width="1282" height="802" data-path="images/dd3e5fe4-shortcutformatter-1ea213ac1d135f72a85bb4dc4dabc50f.png" />
</Frame>

<Frame>
  <img src="https://mintcdn.com/cometchat-22654f5b/-YC7tOebleeoFejE/images/e5a9a16a-shortcutformatter_click_web_screens-b977681014e345970616f9d595132dfb.png?fit=max&auto=format&n=-YC7tOebleeoFejE&q=85&s=210d77ed594a87c21915252b0f240465" width="1282" height="802" data-path="images/e5a9a16a-shortcutformatter_click_web_screens-b977681014e345970616f9d595132dfb.png" />
</Frame>

<Tabs>
  <Tab title="ShortCutFormatter.ts">
    Fetches shortcuts from the Message Shortcuts extension on init. On `keyUp`, checks if the text before the caret matches a shortcut and opens a dialog with the expansion.

    ```ts lines theme={null}
    import { CometChatTextFormatter } from "@cometchat/chat-uikit-react";
    import DialogHelper from "./Dialog";
    import { CometChat } from "@cometchat/chat-sdk-javascript";

    class ShortcutFormatter extends CometChatTextFormatter {
      private shortcuts: { [key: string]: string } = {};
      private dialogIsOpen: boolean = false;
      private dialogHelper = new DialogHelper();
      private currentShortcut: string | null = null;

      constructor() {
        super();
        this.setTrackingCharacter("!");
        CometChat.callExtension("message-shortcuts", "GET", "v1/fetch", undefined)
          .then((data: any) => {
            if (data && data.shortcuts) {
              this.shortcuts = data.shortcuts;
            }
          })
          .catch((error) => console.log("error fetching shortcuts", error));
      }

      onKeyUp(event: KeyboardEvent) {
        const caretPosition =
          this.currentCaretPosition instanceof Selection
            ? this.currentCaretPosition.anchorOffset
            : 0;
        const textBeforeCaret = this.getTextBeforeCaret(caretPosition);

        const match = textBeforeCaret.match(/!([a-zA-Z]+)$/);
        if (match) {
          const shortcut = match[0];
          const replacement = this.shortcuts[shortcut];
          if (replacement) {
            if (this.dialogIsOpen && this.currentShortcut !== shortcut) {
              this.closeDialog();
            }
            this.openDialog(replacement, shortcut);
          }
        } else if (!textBeforeCaret) {
          this.closeDialog();
        }
      }

      getCaretPosition() {
        if (!this.currentCaretPosition?.rangeCount) return { x: 0, y: 0 };
        const range = this.currentCaretPosition?.getRangeAt(0);
        const rect = range.getBoundingClientRect();
        return { x: rect.left, y: rect.top };
      }

      openDialog(buttonText: string, shortcut: string) {
        this.dialogHelper.createDialog(
          () => this.handleButtonClick(buttonText),
          buttonText
        );
        this.dialogIsOpen = true;
        this.currentShortcut = shortcut;
      }

      closeDialog() {
        this.dialogHelper.closeDialog();
        this.dialogIsOpen = false;
        this.currentShortcut = null;
      }

      handleButtonClick = (buttonText: string) => {
        if (this.currentCaretPosition && this.currentRange) {
          const shortcut = Object.keys(this.shortcuts).find(
            (key) => this.shortcuts[key] === buttonText
          );
          if (shortcut) {
            const replacement = this.shortcuts[shortcut];
            this.addAtCaretPosition(
              replacement,
              this.currentCaretPosition,
              this.currentRange
            );
          }
        }
        if (this.dialogIsOpen) {
          this.closeDialog();
        }
      };

      getFormattedText(text: string): string {
        return text;
      }

      private getTextBeforeCaret(caretPosition: number): string {
        if (
          this.currentRange &&
          this.currentRange.startContainer &&
          typeof this.currentRange.startContainer.textContent === "string"
        ) {
          const textContent = this.currentRange.startContainer.textContent;
          if (textContent.length >= caretPosition) {
            return textContent.substring(0, caretPosition);
          }
        }
        return "";
      }
    }

    export default ShortcutFormatter;
    ```
  </Tab>

  <Tab title="Dialog.tsx">
    Helper class that renders a button panel via `CometChatUIEvents.ccShowPanel`.

    ```tsx lines theme={null}
    import React from "react";
    import ReactDOM from "react-dom";
    import { CometChatUIEvents, PanelAlignment } from "@cometchat/chat-uikit-react";

    interface DialogProps {
      onClick: () => void;
      buttonText: string;
    }

    const Dialog: React.FC<DialogProps> = ({ onClick, buttonText }) => {
      return (
        <div style={{ width: "800px", height: "45px" }}>
          <button
            style={{
              width: "800px",
              height: "100%",
              cursor: "pointer",
              backgroundColor: "#f2e6ff",
              border: "2px solid #9b42f5",
              borderRadius: "12px",
              textAlign: "left",
              paddingLeft: "20px",
              font: "600 15px sans-serif, Inter",
            }}
            onClick={onClick}
          >
            {buttonText}
          </button>
        </div>
      );
    };

    export default class DialogHelper {
      private dialogContainer: HTMLDivElement | null = null;

      createDialog(onClick: () => void, buttonText: string) {
        this.dialogContainer = document.createElement("div");
        document.body.appendChild(this.dialogContainer);
        CometChatUIEvents.ccShowPanel.next({
          child: <Dialog onClick={onClick} buttonText={buttonText} />,
          position: PanelAlignment.messageListFooter,
        });
      }

      closeDialog() {
        if (this.dialogContainer) {
          CometChatUIEvents.ccHidePanel.next(PanelAlignment.messageListFooter);
          this.dialogContainer.remove();
          this.dialogContainer = null;
        }
      }
    }
    ```
  </Tab>

  <Tab title="ConversationDemo.tsx">
    Pass the formatter via the `textFormatters` prop on the composer.

    ```tsx lines theme={null}
    import ShortcutFormatter from "./ShortCutFormatter";
    import { CometChatMessageComposer } from "@cometchat/chat-uikit-react";

    <CometChatMessageComposer textFormatters={[new ShortcutFormatter()]} />;
    ```
  </Tab>
</Tabs>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Custom Text Formatter" href="/ui-kit/react/custom-text-formatter-guide">
    Build custom inline text patterns.
  </Card>

  <Card title="Message Composer" href="/ui-kit/react/message-composer">
    Customize the message input component.
  </Card>

  <Card title="All Guides" href="/ui-kit/react/guide-overview">
    Browse all feature and formatter guides.
  </Card>

  <Card title="Sample App" href="https://github.com/cometchat/cometchat-uikit-react/tree/v6/sample-app">
    Full working sample application on GitHub.
  </Card>
</CardGroup>
