General · astro-ui · Command
CommandJS
Inline searchable command palette — grouped actions, keyboard-first.
Command renders a framed palette with a search input on top of a scrollable, grouped action list. Type to filter (case-insensitive substring on labels and keywords), move with ↑/↓, run with Enter or click. Items are declarative: pass ordered groups of { value, label, hint?, shortcut?, disabled?, keywords? } — shortcuts render Kbd-style on the right, hints as a muted second line. Selection emits a bubbling command:select event with the item’s value; run your action from a listener. loop wraps highlighting, vimBindings adds j/k and ctrl+n/ctrl+p, and shouldFilter={false} hands filtering to you for async sources.
Features
- Grouped, ordered sections with uppercase group headings
- Case-insensitive filter across labels + keywords; per-item disabled state
- Full keyboard support: ↑/↓ navigate, Enter runs, Home/End jump; optional loop wrap
- Right-aligned Kbd-style shortcuts (⌘P) and muted hint lines per item
- vimBindings: j/k + ctrl+n/ctrl+p navigation
- Bubbling command:select and command:search-change events
- shouldFilter={false} for async/external filtering
Usage
---
import { Command } from "astro-ui";
---
<Command
label="Commands"
groups={[
{ label: "Suggestions", items: [
{ value: "calendar", label: "Calendar" },
{ value: "calculator", label: "Calculator", disabled: true },
]},
{ label: "Settings", items: [
{ value: "profile", label: "Profile", shortcut: "⌘P" },
]},
]}
/>
<script>
document.addEventListener("command:select", (e) => {
console.log(e.detail.value);
});
</script>Demos
Basic
Suggestions
Settings
No results found.
<Command
label="Commands"
style="max-inline-size:20rem"
groups={[
{ label: "Suggestions", items: [
{ value: "calendar", label: "Calendar" },
{ value: "emoji", label: "Search Emoji" },
{ value: "calculator", label: "Calculator", disabled: true },
]},
{ label: "Settings", items: [
{ value: "profile", label: "Profile", shortcut: "⌘P" },
{ value: "billing", label: "Billing", shortcut: "⌘B" },
{ value: "settings", label: "Settings", shortcut: "⌘S" },
]},
]}
/>
<script>
document.addEventListener("command:select", (e) => {
console.log("run:", e.detail.value);
});
</script>Hints & keywords
Repository
No results found.
<Command
label="Actions"
style="max-inline-size:22rem"
groups={[
{ label: "Repository", items: [
{ value: "push", label: "Push to origin", hint: "main → origin/main", keywords: ["git", "upload"] },
{ value: "pr", label: "Open pull request", hint: "3 files changed", keywords: ["git", "review"] },
{ value: "stash", label: "Stash changes", keywords: ["git", "save"] },
]},
]}
/>Loop + vim bindings
Actions
No results found.
<Command
label="Commands"
loop
vimBindings
autoHighlight
style="max-inline-size:20rem"
groups={[{ label: "Actions", items: [
{ value: "one", label: "First action" },
{ value: "two", label: "Second action" },
{ value: "three", label: "Third action" },
]}]}
/>
<!-- loop wraps the highlight; vimBindings adds j/k + ctrl+n/ctrl+p;
autoHighlight lands on the first item immediately -->API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
groups | { label, items }[] | required | Ordered sections; items are { value, label, hint?, shortcut?, disabled?, keywords? }. |
label | string | "Commands" | Accessible name for the palette. |
defaultSearch | string | "" | Initial text in the search field. |
placeholder | string | "Type a command or search…" | Search placeholder text. |
emptyMessage | string | "No results found." | Shown when nothing matches. |
shouldFilter | boolean | true | Client-side filter; disable for async filtering. |
loop | boolean | false | Highlight wraps from end back to start. |
vimBindings | boolean | false | j/k and ctrl+n/ctrl+p move the highlight. |
autoHighlight | boolean | false | Highlight the first item immediately. |
class | string | — | Merged via cn(); Tailwind classes welcome. |
id | string | — | Element id. |
Accessibility
Implemented accessibility features — semantics, ARIA wiring, and keyboard behavior that ship with the component.
- Root is a labelled group; the search input carries role=combobox with aria-controls and aria-autocomplete=list
- Listbox carries role=listbox; items carry role=option and aria-selected managed by the script
- Disabled items are aria-disabled and skipped by both navigation and Enter
- aria-activedescendant keeps focus in the search field while navigating
- Empty state lives inside the listbox so it is announced with context
FAQ
How is this different from Combobox?
Combobox commits a form value — it has a hidden input and posts with the form. Command runs actions: items are buttons that emit command:select, and nothing is submitted. Use Combobox to pick one of many values, Command to trigger one of many actions.
Can it open in a dialog?
Yes — place Command inside Dialog content and open the dialog from a keyboard shortcut or button. The palette is inline by design, so composition stays explicit.
Changelog
Version history mentioning Command.
- General (7): Button, ButtonGroup, Command, Carousel, Clipboard, Kbd, Link.