Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
React Mentions
https://github.com/hbmartin/react-mentions-ts
Admin
A React component that enables Facebook/Twitter-style @mentions and tagging in textarea inputs with
...
Tokens:
5,252
Snippets:
18
Trust Score:
9.9
Update:
5 months ago
Context
Skills
Chat
Benchmark
79.5
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# React Mentions TS React Mentions TS is a React component library that enables Facebook/Twitter-style @mentions and tagging functionality in textarea inputs with full TypeScript support. The library provides a flexible, accessible, and customizable solution for implementing mention-based interactions in web applications. The component handles the complex task of managing mentions within text input by maintaining two parallel representations: the user-facing plain text display and an internal markup format that preserves mention metadata. It supports multiple trigger patterns (like @ for users, # for tags), both synchronous and asynchronous data sources, custom rendering, and sophisticated keyboard navigation with full ARIA accessibility support. ## Basic Usage Basic implementation with static user data ```tsx import React, { useState } from 'react' import { MentionsInput, Mention } from 'react-mentions-ts' function App() { const [value, setValue] = useState('') const users = [ { id: 'johndoe', display: 'John Doe' }, { id: 'janedoe', display: 'Jane Doe' }, { id: 'alice', display: 'Alice Smith' } ] const handleChange = (event, newValue, newPlainTextValue, mentions) => { setValue(newValue) console.log('Markup value:', newValue) console.log('Plain text:', newPlainTextValue) console.log('Mentions:', mentions) } return ( <MentionsInput value={value} onChange={handleChange} placeholder="Type @ to mention someone" > <Mention trigger="@" data={users} /> </MentionsInput> ) } // Markup value: "Hi @[John Doe](johndoe), how are you?" // Plain text: "Hi John Doe, how are you?" // Mentions: [{ id: 'johndoe', display: 'John Doe', index: 3, plainTextIndex: 3, childIndex: 0 }] ``` ## Single Line Input Creating a single-line mention input instead of textarea ```tsx import React, { useState } from 'react' import { MentionsInput, Mention } from 'react-mentions-ts' function SingleLineExample() { const [value, setValue] = useState('') const data = [ { id: 'user1', display: 'User One' }, { id: 'user2', display: 'User Two' } ] return ( <MentionsInput singleLine value={value} onChange={(e, newValue) => setValue(newValue)} placeholder="Mention people using '@'" > <Mention trigger="@" data={data} /> </MentionsInput> ) } // Renders an <input> element instead of <textarea> // Blocks newlines and Enter key creates mention selection ``` ## Async Data Source Fetching mention suggestions from remote API ```tsx import React, { useState } from 'react' import { MentionsInput, Mention } from 'react-mentions-ts' function AsyncExample() { const [value, setValue] = useState('') const fetchUsers = async (query, callback) => { if (!query) return try { const response = await fetch(`https://api.github.com/search/users?q=${query}`) const data = await response.json() const users = data.items.map(user => ({ id: user.login, display: user.login })) callback(users) } catch (error) { console.error('Failed to fetch users:', error) callback([]) } } return ( <MentionsInput value={value} onChange={(e, newValue) => setValue(newValue)} placeholder="Type @ followed by a username" > <Mention trigger="@" data={fetchUsers} displayTransform={(id) => `@${id}`} /> </MentionsInput> ) } // Data function receives (query, callback) parameters // Callback can be invoked multiple times to update suggestions // Supports both sync return and async callbacks ``` ## Multiple Trigger Patterns Using different triggers for different mention types ```tsx import React, { useState } from 'react' import { MentionsInput, Mention } from 'react-mentions-ts' function MultipleTriggers() { const [value, setValue] = useState('') const users = [ { id: 'johndoe', display: 'John Doe' }, { id: 'janedoe', display: 'Jane Doe' } ] const tags = [ { id: 'urgent', display: 'Urgent' }, { id: 'important', display: 'Important' } ] // Regex trigger for email addresses const emailRegex = /(([^\s@]+@[^\s@]+\.[^\s@]+))$/ return ( <MentionsInput value={value} onChange={(e, newValue) => setValue(newValue)} placeholder="Use @ for users, # for tags, or type an email" > <Mention trigger="@" data={users} markup="@[__display__](user:__id__)" style={{ backgroundColor: '#cee4e5' }} /> <Mention trigger="#" data={tags} markup="#[__display__](tag:__id__)" style={{ backgroundColor: '#ffeaa7' }} /> <Mention trigger={emailRegex} data={(search) => [{ id: search, display: search }]} markup="@[__display__](email:__id__)" style={{ backgroundColor: '#d1c4e9' }} /> </MentionsInput> ) } // Markup: "@[John Doe](user:johndoe) check #[Urgent](tag:urgent) and @[joe@smoe.com](email:joe@smoe.com)" // Each Mention component handles its own trigger pattern // Triggers can be strings or RegExp patterns ``` ## Custom Markup Format Defining custom markup templates for mentions ```tsx import React, { useState } from 'react' import { MentionsInput, Mention } from 'react-mentions-ts' function CustomMarkup() { const [value, setValue] = useState('Hi {{johndoe}}!') const users = [ { id: 'johndoe', display: 'John Doe' }, { id: 'janedoe', display: 'Jane Doe' } ] return ( <MentionsInput value={value} onChange={(e, newValue) => setValue(newValue)} > <Mention trigger="@" data={users} markup="{{__id__}}" displayTransform={(id) => `<-- ${id} -->`} /> </MentionsInput> ) } // Markup template uses __id__ and __display__ placeholders // displayTransform controls how mention appears in plain text // Visual display: "Hi <-- johndoe -->!" // Markup value: "Hi {{johndoe}}!" ``` ## Custom Suggestion Rendering Customizing how suggestions appear in the dropdown ```tsx import React, { useState } from 'react' import { MentionsInput, Mention } from 'react-mentions-ts' function CustomSuggestions() { const [value, setValue] = useState('') const users = [ { id: 'johndoe', display: 'John Doe', email: 'john@example.com', avatar: '👨' }, { id: 'janedoe', display: 'Jane Doe', email: 'jane@example.com', avatar: '👩' } ] const renderSuggestion = (suggestion, search, highlightedDisplay, index, focused) => ( <div className={`user-suggestion ${focused ? 'focused' : ''}`}> <span className="avatar">{suggestion.avatar}</span> <div className="info"> <div className="name">{highlightedDisplay}</div> <div className="email">{suggestion.email}</div> </div> </div> ) return ( <MentionsInput value={value} onChange={(e, newValue) => setValue(newValue)} > <Mention trigger="@" data={users} renderSuggestion={renderSuggestion} /> </MentionsInput> ) } // renderSuggestion receives (suggestion, query, highlightedDisplay, index, focused) // highlightedDisplay includes automatic query highlighting // focused indicates if suggestion is keyboard-selected // Custom data properties are accessible in suggestion object ``` ## Styling Options Applying styles using inline styles, CSS classes, or CSS modules ```tsx import React, { useState } from 'react' import { MentionsInput, Mention } from 'react-mentions-ts' // Inline styles approach const customStyle = { control: { backgroundColor: '#fff', fontSize: 14, fontWeight: 'normal', }, highlighter: { overflow: 'hidden', }, input: { margin: 0, padding: '9px', border: '1px solid silver', }, suggestions: { list: { backgroundColor: 'white', border: '1px solid rgba(0,0,0,0.15)', fontSize: 14, }, item: { padding: '5px 15px', borderBottom: '1px solid rgba(0,0,0,0.15)', '&focused': { backgroundColor: '#cee4e5', }, }, }, } const mentionStyle = { backgroundColor: '#cee4e5', } function StyledExample() { const [value, setValue] = useState('') return ( <MentionsInput value={value} onChange={(e, newValue) => setValue(newValue)} style={customStyle} > <Mention trigger="@" data={[{ id: 'user1', display: 'User One' }]} style={mentionStyle} /> </MentionsInput> ) } // CSS classes approach function CSSClassExample() { const [value, setValue] = useState('') return ( <MentionsInput value={value} onChange={(e, newValue) => setValue(newValue)} className="mentions-input" > <Mention trigger="@" data={[{ id: 'user1', display: 'User One' }]} className="mention" /> </MentionsInput> ) } // CSS modules approach import styles from './mentions.module.css' function CSSModulesExample() { const [value, setValue] = useState('') return ( <MentionsInput value={value} onChange={(e, newValue) => setValue(newValue)} classNames={styles} > <Mention trigger="@" data={[{ id: 'user1', display: 'User One' }]} classNames={styles} /> </MentionsInput> ) } // Style supports nested object structure for all component parts // className generates derived class names (e.g., "mentions-input__input") // classNames for CSS modules provides scoped class name mapping ``` ## Event Callbacks Handling mention lifecycle events and user interactions ```tsx import React, { useState } from 'react' import { MentionsInput, Mention } from 'react-mentions-ts' function EventsExample() { const [value, setValue] = useState('') const handleChange = (event, newValue, newPlainTextValue, mentions) => { setValue(newValue) console.log('Value changed:', { markup: newValue, plainText: newPlainTextValue, mentions: mentions }) } const handleMentionAdd = (id, display, startPos, endPos) => { console.log(`Mention added: ${display} (${id}) at positions ${startPos}-${endPos}`) } const handleKeyDown = (event) => { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault() console.log('Submit:', value) } } const handleBlur = (event, clickedSuggestion) => { console.log('Input blurred, clicked suggestion:', clickedSuggestion) } return ( <MentionsInput value={value} onChange={handleChange} onKeyDown={handleKeyDown} onBlur={handleBlur} > <Mention trigger="@" data={[{ id: 'user1', display: 'User One' }]} onAdd={handleMentionAdd} appendSpaceOnAdd /> </MentionsInput> ) } // onChange: (event, newValue, newPlainTextValue, mentions) => void // onAdd: (id, display, startPos, endPos) => void // onBlur: (event, clickedSuggestion: boolean) => void // onKeyDown: (event) => void // appendSpaceOnAdd automatically adds space after mention ``` ## Programmatic Control Controlling the input programmatically via refs ```tsx import React, { useState, useRef, useEffect } from 'react' import { MentionsInput, Mention } from 'react-mentions-ts' function ProgrammaticExample() { const [value, setValue] = useState('') const inputRef = useRef(null) const focusInput = () => { inputRef.current?.focus() } const insertMention = () => { setValue('@[John Doe](johndoe) ') setTimeout(() => focusInput(), 0) } useEffect(() => { // Auto-focus on mount focusInput() }, []) return ( <div> <MentionsInput value={value} onChange={(e, newValue) => setValue(newValue)} inputRef={inputRef} > <Mention trigger="@" data={[{ id: 'johndoe', display: 'John Doe' }]} /> </MentionsInput> <button onClick={focusInput}>Focus Input</button> <button onClick={insertMention}>Insert Mention</button> </div> ) } // inputRef provides access to underlying input/textarea element // Supports both function refs and React.RefObject // Element reference: HTMLInputElement | HTMLTextAreaElement ``` ## Advanced Configuration Portal rendering, accessibility, and space handling ```tsx import React, { useState } from 'react' import { MentionsInput, Mention } from 'react-mentions-ts' function AdvancedConfig() { const [value, setValue] = useState('') const portalHost = document.getElementById('portal-root') return ( <MentionsInput value={value} onChange={(e, newValue) => setValue(newValue)} suggestionsPortalHost={portalHost} allowSuggestionsAboveCursor allowSpaceInQuery a11ySuggestionsListLabel="Suggested user mentions" customSuggestionsContainer={(children) => ( <div className="custom-suggestions-wrapper"> {children} </div> )} > <Mention trigger="@" data={[{ id: 'user1', display: 'User One' }]} allowSpaceInQuery /> </MentionsInput> ) } // suggestionsPortalHost: renders suggestions in different DOM location // allowSuggestionsAboveCursor: positions suggestions above if no space below // forceSuggestionsAboveCursor: always position above cursor // allowSpaceInQuery: keeps suggestions open when space is typed // a11ySuggestionsListLabel: ARIA label for screen readers // customSuggestionsContainer: wraps suggestions list with custom component ``` ## Utility Functions Extracting mentions and plain text from markup values ```tsx import { getMentions, getPlainText } from 'react-mentions-ts/utils' function UtilitiesExample() { const markupValue = "Hi @[John Doe](johndoe), check #[Urgent](urgent)" // Configuration from Mention components const config = [ { markup: '@[__display__](user:__id__)', regex: /@\[([^\]]+)\]\(user:([^)]+)\)/g, displayTransform: (id, display) => display || id }, { markup: '#[__display__](tag:__id__)', regex: /#\[([^\]]+)\]\(tag:([^)]+)\)/g, displayTransform: (id, display) => display || id } ] // Extract plain text const plainText = getPlainText(markupValue, config) console.log(plainText) // Output: "Hi John Doe, check Urgent" // Extract mentions const mentions = getMentions(markupValue, config) console.log(mentions) // Output: [ // { id: 'johndoe', display: 'John Doe', childIndex: 0, index: 3, plainTextIndex: 3 }, // { id: 'urgent', display: 'Urgent', childIndex: 1, index: 31, plainTextIndex: 20 } // ] return null } // getPlainText(value, config): converts markup to plain text // getMentions(value, config): extracts all mention occurrences // config array matches structure of Mention components // index: position in markup, plainTextIndex: position in plain text ``` ## TypeScript Types Full TypeScript type definitions for all props and data structures ```tsx import type { MentionsInputProps, MentionComponentProps, MentionDataItem, MentionsInputChangeHandler, MentionOccurrence } from 'react-mentions-ts' // Data item structure const user: MentionDataItem = { id: 'johndoe', // string | number (required) display: 'John Doe', // string (optional, defaults to id) email: 'john@example.com', // any additional properties allowed avatar: 'https://...' } // Async data provider function type type DataProviderFn = ( query: string ) => MentionDataItem[] | Promise<MentionDataItem[]> // Change handler type const handleChange: MentionsInputChangeHandler = ( event, // ChangeEvent<HTMLInputElement | HTMLTextAreaElement> newValue, // string (markup) newPlainTextValue, // string (plain text) mentions // MentionOccurrence[] ) => { console.log({ event, newValue, newPlainTextValue, mentions }) } // Mention occurrence type interface MentionOccurrence { id: string display: string childIndex: number // which Mention component index: number // position in markup plainTextIndex: number // position in plain text } // All component props are fully typed const mentionsInputProps: MentionsInputProps = { value: '', onChange: handleChange, singleLine: false, allowSpaceInQuery: false, allowSuggestionsAboveCursor: false, forceSuggestionsAboveCursor: false, a11ySuggestionsListLabel: '', children: <Mention trigger="@" data={[]} /> } // Excellent IDE autocomplete and type checking // Generic types available for custom data structures ``` ## Summary React Mentions TS provides a production-ready solution for implementing mention functionality in React applications. The library handles the complexity of maintaining synchronized markup and plain text representations while offering extensive customization through props, styling options, and lifecycle callbacks. Core use cases include social media-style user mentions, tag systems, autocomplete for structured data, and any interface requiring inline entity references within text input. Integration is straightforward with both controlled and uncontrolled patterns supported. The component works seamlessly with form libraries, state management solutions, and server-side rendering. Key features include keyboard navigation (arrow keys, Enter, Tab, ESC), copy-paste support that preserves mention metadata, mobile device compatibility, and comprehensive accessibility through ARIA attributes. The TypeScript-first implementation ensures type safety across all interactions while maintaining compatibility with JavaScript projects through automatic type inference and optional gradual typing adoption.