Slash command menu
A composer where typing / opens a filterable list of commands, and the pick becomes a chip in front of the message.
Loading preview…
Install
- 1
Run this command in your Tailwind CSS project. It adds
components/ai/slash-command-menu.tsxand installs its dependencies.$ npx shadcn@latest add https://gradientdeck.com/ai-components/r/slash-command-menu.json
Usage & API
Example
import { SlashCommandMenu } from "@/components/ai/slash-command-menu"
<SlashCommandMenu
commands={[]}
value="…"
onChange={() => {}}
command={null}
onCommandChange={() => {}}
onSubmit={() => {}}
/>Props SlashCommandMenu
| Prop | Type & description | Required |
|---|---|---|
commands | SlashCommand[] | Required |
value | string | Required |
onChange | (value: string) => void | Required |
command | SlashCommand | null | Required |
onCommandChange | (command: SlashCommand | null) => void | Required |
onSubmit | (value: string, command: SlashCommand | null) => void | Required |
placeholder | string | Optional |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
The only variant is Default, and the export is SlashCommandMenu. The menu pops above the field, aligned to the card with a small inset, and the heading inside it is Commands. Each row shows a slash plus the id, then the hint, then the icon in a small tile that tints when the row is active. Mouse enter moves the active index. Mouse down on a row prevents the default so the textarea does not blur before the pick. The textarea grows up to 160 pixels. Clicking the card focuses it. After a send, the demo clears both the text and the command and writes a polite live line, Sent, plus the label and the text, for about 2.4 seconds. That confirmation is demo state, not a prop. If you need the chip to survive a send, keep command set. Passing null is how you clear it.
Choosing a layout
Use Slash command menu when the product has a small set of modes that should be chosen in the text, not from a toolbar. The demo list is Summarize, Translate, To-do list, Debug, Search the web and Improve writing, each with a one-line hint. That shape fits a chat where /summarize means something to your API and the rest of the textarea is the subject. The chip is the record of the choice. You still decide what the id means when onSubmit fires. The component does not run the command.
Use Mention menu instead when the token is an @ and the result should sit inline in the sentence, grouped as a file, a person or a doc. Use Prompt input when there is no list. The workspace placeholder talks about slash commands, but it does not filter or chip them. Do not put both menus on one keystroke without deciding which component owns the textarea. This one owns the field, the list and the chip together.
Why does the menu close when I press Escape and stay closed while I keep typing the same slash?
Escape stores the current value as dismissed. The list opens only when a slash query exists and dismissed is not that exact string. The next onChange clears the flag, so deleting a character or adding one opens it again. The match also has to be at the end of the text. If you move the caret back and type in front of the slash, the query no longer matches and the list closes for that reason instead.
Can someone send a command without any extra text?
Yes. canSend is true when the trimmed value is non-empty or command is not null. Enter, outside the menu, calls onSubmit with the text and the command. A chip alone is enough. Removing the chip with its button, or with Backspace on an empty field, sets the command back to null. The slash token itself is not part of the submitted string. It is stripped when the row is picked.