# Composer Trigger Popover URL: /docs/ui/composer-trigger-popover Reusable picker UI for @ mentions, / slash commands, and any other character-triggered popover. Getting Started \[#getting-started] Add `composer-trigger-popover` \[#add-composer-trigger-popover] This adds `/components/assistant-ui/composer-trigger-popover.tsx` — a generic picker UI (Categories + Items + Back) driven by an adapter and one of two behavior props: `directive` (insert a chip) or `action` (run a callback). Wrap the composer \[#wrap-the-composer] Place `ComposerPrimitive.Unstable_TriggerPopoverRoot` around your composer. Any number of `ComposerTriggerPopover` declarations can live inside — each with its own trigger character, adapter, and behavior prop. ```tsx title="components/assistant-ui/thread.tsx" import { ComposerPrimitive } from "@assistant-ui/react"; import { ComposerTriggerPopover } from "@/components/assistant-ui/composer-trigger-popover"; const Composer = () => ( {/* triggers declared here */} ); ``` @ Mention \[#-mention] Pair the popover with `unstable_useMentionAdapter` — the hook returns a spreadable `{ adapter, directive }` bundle so selecting an item writes a `:tool[Label]{name=id}` directive into the composer text. ```tsx import { unstable_useMentionAdapter } from "@assistant-ui/react"; import { WrenchIcon } from "lucide-react"; const mention = unstable_useMentionAdapter(); ; ``` Override formatter or add an `onInserted` callback via hook options: `unstable_useMentionAdapter({ formatter, onInserted })`. `unstable_useMentionAdapter` also accepts `items` (flat custom list), `categories` (multi-category drill-down), and `includeModelContextTools` for fine-grained control. See the [Mentions guide](/docs/guides/mentions#built-in-mention-adapter). Render selected mentions as chips in user messages with [`DirectiveText`](/docs/ui/directive-text). For inline chips **inside** the composer, use [`LexicalComposerInput`](/docs/guides/mentions#textarea-vs-lexical). / Slash Command \[#-slash-command] Use [`unstable_useSlashCommandAdapter`](/docs/guides/slash-commands) to bundle commands (data + `execute`) into `{ adapter, action }` — then plug both into `ComposerTriggerPopover`. By default a directive chip is left in the composer as an audit trail; pass `removeOnExecute` to strip the `/command` text entirely. `iconMap` maps `metadata.icon` strings on items and categories to Lucide icons. ```tsx import { unstable_useSlashCommandAdapter, type Unstable_SlashCommand, } from "@assistant-ui/react"; import { FileTextIcon, GlobeIcon, LanguagesIcon, SlashIcon } from "lucide-react"; const SLASH_COMMANDS: readonly Unstable_SlashCommand[] = [ { id: "summarize", description: "Summarize the conversation", icon: "FileText", execute: () => {/* ... */}, }, { id: "translate", description: "Translate to another language", icon: "Languages", execute: () => {/* ... */}, }, { id: "search", description: "Search the web", icon: "Globe", execute: () => {/* ... */}, }, ]; function SlashComposer() { const slash = unstable_useSlashCommandAdapter({ commands: SLASH_COMMANDS }); return ( ); } ``` Combining Triggers \[#combining-triggers] Multiple popovers coexist under one `TriggerPopoverRoot`. Each reads state from its own declaration, so `@` and `/` never collide. ```tsx const commandHandlers: Record void> = { summarize: () => {/* ... */}, translate: () => {/* ... */}, }; commandHandlers[item.id]?.(), }} iconMap={slashIcons} fallbackIcon={SlashIcon} /> ``` Keyboard Navigation \[#keyboard-navigation] | Key | Action | | -------------------- | --------------------------------------------- | | ArrowDown | Highlight next item | | ArrowUp | Highlight previous item | | Enter | Select highlighted item / drill into category | | Escape | Close popover | | Backspace | Go back to categories (when query is empty) | API Reference \[#api-reference] | Prop | Type | Default | Description | | ---------------------- | --------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------- | | `char` | `string` | — | Trigger character, e.g. `"@"` or `"/"` (required; unique within the root) | | `adapter` | `Unstable_TriggerAdapter` | — | Provides categories, items, and search (required) | | `directive` | `{ formatter, onInserted?, chip? }` | — | Enables directive-insert behavior. Mutually exclusive with `action`. | | `action` | `{ formatter, onExecute, removeOnExecute?, chip? }` | — | Enables action behavior. Mutually exclusive with `directive`. | | `iconMap` | `Record` | — | Maps `item.metadata.icon` / `category.metadata.icon` strings to icons | | `fallbackIcon` | `IconComponent` | `SparklesIcon` | Icon used when no `iconMap` entry matches | | `backLabel` | `string` | `"Back"` | Back button label | | `emptyCategoriesLabel` | `string` | `"No items available"` | Shown when no categories are available | | `emptyItemsLabel` | `string` | `"No matching items"` | Shown when no items match | All other props (`className`, etc.) forward to the underlying popover `div`. `directive` object \[#directive-object] | Field | Type | Description | | ------------ | ----------------------------- | ---------------------------------------------------------------------------- | | `formatter` | `Unstable_DirectiveFormatter` | Serializes the selected item into the directive text written to the composer | | `onInserted` | `(item) => void` | Optional callback fired after the directive has been inserted | `action` object \[#action-object] | Field | Type | Description | | ----------------- | ----------------------------- | -------------------------------------------------------------------------------------- | | `formatter` | `Unstable_DirectiveFormatter` | Serializes the selected item into the chip left behind (unused when `removeOnExecute`) | | `onExecute` | `(item) => void` | Callback fired when an item is selected | | `removeOnExecute` | `boolean` | When `true`, strips the trigger text instead of leaving a chip. Default `false`. | Related \[#related] * [Directive Text](/docs/ui/directive-text) — renderer for mention chips in user messages * [Mentions guide](/docs/guides/mentions) — `@`-mention architecture and formatter details * [Slash Commands guide](/docs/guides/slash-commands) — `/`-command architecture