Skip to content
Kura UI

Slash Command Menu

beta

Inline “/” command menu anchored to the caret of a textarea, with filtering, groups, icons, descriptions and keyboard navigation.

@kura-ui/react/slash-command-menuCopy page for agents.mdregistry.json

On a plain textarea

minimal

Groups, icons, shortcuts and actions

Ten commands in four groups, a disabled command, keywords (/tldr finds Summarize) and an action that clears the input.

full

Type / at the start of a line

Inside a composer

Pass the ComposerInput ref; Enter inserts the command instead of sending while the menu is open.

composition
Commands/SendEnter

Installation

npx shadcn@latest add https://kuro.abstergo.fyi/r/slash-command-menu.json

Copies the source into components/kura/slash-command-menu with its dependencies (kura-theme, kbd, kura-keys, utils). Tokens come from the kura-theme item.

When to use it

Use it for

  • Let users insert prompt templates or run actions by typing “/” in a composer (/summarize, /debug, /clear).
  • Any textarea or input that needs caret-anchored suggestions triggered by a character (use `trigger` for “@” mentions or “#” tags).

Reach for something else when

  • A global ⌘K launcher for navigation and actions — use command-palette.
  • Choosing a model from a toolbar — use model-picker.
  • Static clickable starters in an empty chat — use prompt-suggestions.

Props

PropTypeDefaultDescription
textareaRef*React.RefObject<HTMLTextAreaElement | HTMLInputElement | null>—Ref to the field to watch, e.g. the `ref` you pass to ComposerInput. The menu never takes focus from it.
commands*readonly SlashCommand[]—Commands: { id, label, description?, icon?, group?, keywords?, shortcut?, insert?, disabled? }.
onSelect(command: SlashCommand, details: SlashCommandSelectDetails) => void—Called after the typed “/query” has been replaced by `command.insert` (or removed). details.query is what was typed.
onOpenChange(open: boolean) => void—Called when the menu opens or closes.
triggerstring"/"Trigger character. Only fires at the start of a line or after whitespace.
side"auto" | "top" | "bottom""auto"auto opens below the caret and flips above when there is no room.
emptyMessagestring | null"No commands"Shown when nothing matches; null closes the menu instead.
disabledbooleanfalseStop watching the field without unmounting.

All native attributes of the root element, className and ref pass through.

Anatomy

  1. SlashCommandMenuFloating panel portalled to <body>, position: fixed at the trigger character. data-side = top | bottom; scales from the caret corner.data-slot="slash-command-menu"
  2. SlashCommandMenuGrouprole=group per `group`, labelled by its heading.data-slot="slash-command-menu-group"
  3. SlashCommandMenuItemrole=option row: icon tile, label (matched text emphasised), description, shortcut. data-active, data-disabled.data-slot="slash-command-menu-item"
  4. SlashCommandMenuFooterKey hints: ↑↓ navigate, ↵ insert, Esc dismiss.data-slot="slash-command-menu-footer"

Accessibility

  • Role: listbox
  • Focus never leaves the textarea. While open, the textarea gets aria-controls, aria-autocomplete=list and aria-activedescendant pointing at the highlighted option, so screen readers read the option as you arrow.
  • A polite status announces the number of matching commands.
  • Pointer selection uses mousedown preventDefault so the textarea keeps focus and its caret.
  • Insertion uses the browser's insertText command where available, so ⌘Z undoes a command like normal typing.
  • Reduced motion: The menu fades in without the scale and 2px travel.
KeysAction
/Open (at line start or after a space)
ArrowDown / ArrowUpMove the highlight (wraps, skips disabled)
Enter / TabInsert the highlighted command
EscapeDismiss for this “/” (typing on does not reopen it)
Space / Backspace past /Closes the menu

Notes for agents

  • Render the menu next to the field (anywhere in the tree); it portals itself to <body> and positions at the caret.
  • It works with controlled fields: the replacement dispatches a native input event, so React onChange / Composer onValueChange fire.
  • Use `insert` for prompt templates; omit it for actions and handle them in onSelect (e.g. id === "clear").
  • Its keydown listener runs before React handlers and stops Enter/Tab/arrows while open, so Composer will not send mid-selection.
  • getCaretRect(field, offset) is exported if you need caret coordinates for something else.