Docs
🪄 ⏐ Examples
Mentions

Mentions

reachat 2.1 introduces rich text input with @ mentions support powered by Tiptap v3 (opens in a new tab). Users can mention people, teams, or any entity by typing @ followed by a search query.

Overview

The mention feature provides:

  • Floating suggestion popup with keyboard navigation
  • Dynamic/async search for mentions
  • Customizable mention items with icons and descriptions
  • Custom rendering for mention suggestions
  • Full keyboard support (arrow keys, enter, escape)

Basic Usage

To enable mentions, configure the RichTextInput component with a mentions prop:

import { RichTextInput } from 'reachat';
 
const mentionItems = [
  { id: '1', label: 'John Doe', description: 'Product Manager' },
  { id: '2', label: 'Jane Smith', description: 'Engineering Lead' },
  { id: '3', label: 'Engineering Team', description: 'Team mention' }
];
 
<RichTextInput
  mentions={{
    trigger: '@',
    items: mentionItems
  }}
  onSubmit={(value) => console.log(value)}
/>

Configuration Options

The mentions prop accepts a SuggestionConfig object with the following options:

Static Items

<RichTextInput
  mentions={{
    trigger: '@',
    items: [
      {
        id: 'user1',
        label: 'Alice Johnson',
        description: 'Senior Developer',
        icon: <UserIcon />
      },
      {
        id: 'user2',
        label: 'Bob Williams',
        description: 'Design Lead'
      }
    ],
    maxResults: 5
  }}
/>

Dynamic Search

For large datasets or remote data sources, use the onSearch callback:

<RichTextInput
  mentions={{
    trigger: '@',
    onSearch: async (query) => {
      const response = await fetch(`/api/users?q=${query}`);
      const users = await response.json();
      return users.map(user => ({
        id: user.id,
        label: user.name,
        description: user.role,
        metadata: { email: user.email }
      }));
    },
    maxResults: 10
  }}
/>

Custom Selection Handler

Control what happens when a mention is selected:

<RichTextInput
  mentions={{
    trigger: '@',
    items: mentionItems,
    onSelect: (item, insertText) => {
      // Custom logic before inserting
      console.log('Mentioned:', item);
 
      // Insert custom format
      insertText(`@${item.label} `);
    }
  }}
/>

Custom Rendering

Customize how mentions appear in the suggestion list:

<RichTextInput
  mentions={{
    trigger: '@',
    items: mentionItems,
    renderItem: (item, isHighlighted) => (
      <div style={{
        padding: '8px 12px',
        backgroundColor: isHighlighted ? '#f0f0f0' : 'transparent',
        display: 'flex',
        alignItems: 'center',
        gap: '8px'
      }}>
        {item.icon}
        <div>
          <div style={{ fontWeight: 'bold' }}>{item.label}</div>
          <div style={{ fontSize: '12px', color: '#666' }}>
            {item.description}
          </div>
        </div>
      </div>
    ),
    renderEmpty: (query) => (
      <div style={{ padding: '8px 12px', color: '#999' }}>
        No users found matching "{query}"
      </div>
    )
  }}
/>

Using with ChatInput

The ChatInput component automatically supports mentions when configured:

import { Chat, ChatInput } from 'reachat';
 
<Chat
  sessions={sessions}
  activeSessionId={activeId}
>
  <SessionMessagePanel>
    <SessionMessagesHeader />
    <SessionMessages />
    <ChatInput
      mentions={{
        trigger: '@',
        onSearch: searchUsers,
        renderItem: (item, isHighlighted) => (
          <MentionItemComponent item={item} highlighted={isHighlighted} />
        )
      }}
    />
  </SessionMessagePanel>
</Chat>

Advanced Examples

Multiple Trigger Characters

You can combine mentions with commands and other triggers:

<RichTextInput
  mentions={{
    trigger: '@',
    items: userMentions
  }}
  commands={{
    trigger: '/',
    items: slashCommands
  }}
/>

Metadata and Custom Actions

Store additional data with mentions:

const mentions = {
  trigger: '@',
  items: [
    {
      id: 'channel1',
      label: 'general',
      description: 'General discussion',
      metadata: {
        type: 'channel',
        memberCount: 150
      }
    }
  ],
  onSelect: (item, insertText) => {
    if (item.metadata?.type === 'channel') {
      // Special handling for channel mentions
      insertText(`#${item.label} `);
    } else {
      insertText(`@${item.label} `);
    }
  }
};

Keyboard Navigation

Users can navigate the mention list using:

  • Arrow Up/Down: Move selection
  • Enter: Select current item
  • Escape: Close suggestion popup
  • Continue typing: Filter results

API Reference

SuggestionConfig Interface

interface SuggestionConfig<T extends SuggestionItem = SuggestionItem> {
  trigger?: string;
  items?: T[];
  onSearch?: (query: string) => Promise<T[]> | T[];
  onSelect?: (item: T, insertText: (text: string) => void) => void;
  maxResults?: number;
  renderItem?: (item: T, isHighlighted: boolean) => ReactNode;
  renderEmpty?: (query: string) => ReactNode;
}

MentionItem Interface

interface MentionItem extends SuggestionItem {
  id: string;
  label: string;
  description?: string;
  icon?: ReactElement;
  value?: string; // Custom value to insert
  metadata?: Record<string, unknown>;
}

For more examples and interactive demos, visit the storybook demos (opens in a new tab).