Skip to content

Command ​

A command palette (⌘K to invoke, hotkey configurable) — search filtering, score-based ranking, keyboard selection, Enter to execute, nested sub-pages, recent history and multi-select batch run. open is controlled: it can be set externally, and the configurable global shortcut or Esc closes it (each open/close fires one oas-open-change).

Basic usage ​

Basic usage (open with ⌘J / Ctrl+J)

Press ⌘J / Ctrl+J to open the command palette, or control open externally (the docs site search uses Ctrl+K, so this demo uses Ctrl+J)

Controlled open ​

The open attribute is externally controlled: an external button sets open to open the palette; it closes via Esc / backdrop click / selecting a command (the component removes open; the host syncs state through oas-open-change).

When open, the backdrop covers the full screen, so no external "close" button is provided — use Esc / click the backdrop / select a command to close.

Externally controlled open (sync via oas-open-change)

Open command paletteopen: falseNothing selected

Groups and empty state ​

Group titles render from the group field; an empty state shows when nothing matches (plain "No matching commands" when the query is empty, or "No commands found for "word"" with a search term).

Groups and empty state

Open (try searching "deploy" and "xyz")Nothing selected

Icons · descriptions · shortcut labels ​

Item fields icon (SVG path d or a full <svg> markup), description (secondary line) and shortcut (right-aligned kbd with meta/ctrl/shift/alt symbol mapping).

Icon + description + shortcut

OpenNothing selected

Configurable invocation shortcut ​

The hotkey attribute configures the combo (mod/meta/ctrl/alt/shift, comma-separated for multiple); false disables the built-in listener. Default is mod+k (⌘K / Ctrl+K).

Custom shortcut (ctrl+shift+p) and disabled

closed (press ctrl+shift+p to open)Open externallyThe instance below has hotkey="false" (⌘K will not open it)

Match highlighting ​

While searching, the matched characters in the label are wrapped in <mark> — instant feedback as you type.

Search highlight

Open (type "file" to see highlighting)Nothing selected

External filtering (should-filter=false) ​

should-filter="false" disables built-in filtering and ranking — filtering is fully delegated to the host: the component fires oas-input as you type, and the host requests an async data source and writes back items. This demo simulates a 600ms server search; when there are no results it returns a forceMount "create" entry (rendered regardless of filtering).

should-filter=false + async data source

Open (type "open")All commands

Empty-state slot ​

slot="empty" customizes the empty result rendering (replacing the default "No commands found…" text); the host reads the current query via el.query. Example: a "create xxx" button when nothing matches.

Empty-state slot

No matching commandsCreate "…"
Open (search "xyz" to see the slot)Nothing created

Nested pages / breadcrumb fallback ​

The item field page (array of commands) defines a sub-page: selecting it enters the sub-page (a breadcrumb bar with a back button appears at the top), Esc or Backspace with an empty query goes back, and only at the root does Esc close the panel. Each push/pop fires one oas-page-change.

Nested pages

Open (select "Change theme" to enter the sub-page)Nothing selected

Declarative child-element channel ​

Besides the items JSON, commands can be declared with <oas-command-item> child elements (items takes precedence when explicitly set; otherwise the child elements are parsed and converge onto the same rendering path). The default slot text becomes the label, and attributes align with the items fields: value / group / disabled / icon / shortcut / description / view / force-mount / separator; keywords is a comma-separated string participating in search matching. Nested <oas-command-item> elements directly inside an item recursively become its page sub-page. Child additions/removals, attribute and text changes re-render automatically (MutationObserver).

Declarative children (groups / separator / shortcuts / nested sub-page)

New fileOpen fileSeparatorChange theme LightDarkUndoOpen (no `items` set — uses the child channel)Dynamically append an itemNothing selected

Recent history ​

recent tracks recently used commands: selected items are pinned to the top (deduped, capped at 10); recent-storage-key enables localStorage persistence (restored across instances). This demo swaps the item set after selection to simulate "command set changes" — reopen to see the "Recent" group.

Recent history (localStorage persistence)

Open (select a few items, then reopen to see recents pinned)Nothing selected

Loading and limit ​

loading shows an async loading placeholder (spinner row + aria-busy); limit caps the rendered item count (default 50).

Loading and limit

Open first and toggle loadingOpen second (limit=3)limit renders only the first 3 items

Multiple-select commands ​

multiple mode: Enter/click toggles checks (fires oas-change { values }), and a "Run n" confirm button in the footer executes the batch (fires oas-select { values }).

Multi-select batch run

Open (check several items, then press "Run n" in the footer)Nothing executed

In-panel views (Raycast style) ​

The item field view defines a view: selecting it renders the form/panel carried by <slot name="view-{view}"> (deploy params, quick actions…), Esc/breadcrumb goes back. Each enter/exit fires one oas-view-change.

View slot (deploy form)

Run deployEsc returns to the command list
Send notification
Open (select "Deploy app" to enter the form)Nothing done

Controlled query and active item ​

value is the controlled search query (two-way by echoing oas-input back); selected is the controlled active item (the host echoes oas-active back). close-on-select="false" keeps the panel open after selection for consecutive actions.

Controlled value / selected

Open (value/selected two-way controlled)value: fileactive: open-fileNothing executed

slot="footer" replaces the default shortcut hints (↑↓ navigate / ↵ select / esc close).

Footer slot

↑↓ navigate · ↵ select · esc closeOpen (see the custom footer hints)Nothing selected

Mount container (append-to) ​

append-to: the whole panel (backdrop + palette) moves into a portal host inside the target container (isolated shadow + style injection + slot bridging — empty/footer/view-* slot content travels with the panel), for nested transform / stacking-context scenarios.

append-to mount container

Open (the panel mounts into the container below)Nothing selected↑↓ navigate · ↵ select · esc close (travels with the panel)

Virtual scrolling ​

virtual enables windowed rendering for large datasets (reuses oas-virtual-list; falls back to full rendering when groups or recents are present). This demo preloads 20,000 commands and renders only the visible window.

Virtual scrolling (20k items)

Open (20k items, arrow keys to scroll)Nothing selected

API ​

oas-command ​

Attributes ​

AttributeDescriptionTypeDefault
append-toMount container selector (e.g. #panel): the whole panel (backdrop + palette) moves into a portal host inside the target container (isolated shadow + style injection + slot bridging — empty/footer/view-* slots travel with the panel), for nested transform/stacking-context scenarios; without it the panel is fixed-positioned inside the component's own shadowstring—
close-on-selectClose the panel after selecting (default true; false keeps it open for consecutive actions)string—
hotkeyInvocation shortcut combos, e.g. ctrl+k / meta+shift+p (comma-separated for multiple; supports mod/meta/ctrl/alt/shift); false disables the built-in listener (default mod+k)stringmod+k
item-heightVirtual row height (default 36)string36
itemsCommand items JSON (root page; sub-pages via item.page, supports icon / shortcut / description / page / view / forceMount / separator fields)CommandItem[] | string[]
limitMax rendered items (default 50; not applied in virtual mode)string50
loadingAsync loading placeholder (loading row in the list + aria-busy)boolean—
multipleMultiple-select mode (toggle checks + batch run via the footer confirm button)boolean—
openWhether open (controlled; auto-removed after selection / Esc; fires oas-open-change on open and close)boolean—
recentTrack recently used (selected items pinned to top, deduped, capped at 10)boolean—
recent-storage-keylocalStorage key for recent persistence (requires recent; restored across instances)string—
selectedActive item value (controlled; arrow keys / hover fire oas-active for the host to echo back)string—
should-filterfalse disables built-in filtering and score sorting (filtering delegated to the host, for async/server data sources)stringtrue
valueSearch query (controlled; two-way via oas-input echo-back)string—
virtualVirtual scrolling for large datasets (reuses oas-virtual-list)boolean—

Events ​

EventDescription
oas-activeActive item changed, detail: { value } (basis for controlled selected echo-back)
oas-changeMulti-select toggle, detail: { values }
oas-inputSearch input, detail: { value } (request channel for should-filter=false external filtering)
oas-open-changePanel opened/closed, detail: { open }
oas-page-changeSub-page pushed/popped, detail: { title, depth, direction } (direction: push|pop)
oas-selectA command was executed, detail: { value }; multi-select confirm detail: { values }
oas-view-changeView entered/exited, detail: { view, title } (exiting sends view: '')

Slots ​

NameDescription
emptyCustom empty state (read the current query via el.query, e.g. a "create xyz" entry)
footerCustom footer bar (defaults to ↑↓ navigate / ↵ select / esc close hints)
view-${view}Per-view override of the selected content (host writes <div slot="view-deploy"> …; view is the view field value)

oas-command-item ​

Attributes ​

AttributeDescriptionTypeDefault
descriptionSecondary description line——
disabledDisable this item (not selectable via Enter/click, skipped by arrow keys)——
force-mountIgnore filtering and always render (create-type entry)——
groupGroup name (optional); same-group items render a group title——
iconItem icon: SVG path d string or a full <svg> markup——
keywordsSearch keywords (comma-separated string, trimmed into string[]), matched in addition to the label——
separatorRender as a separator row (not navigable/selectable)——
shortcutShortcut label (right-aligned kbd), e.g. meta+p / ctrl+shift+s——
valueSelection value (data-carrier field of the declarative child channel)——
viewView slot name: selecting enters <slot name="view-{view}"> (in-panel view)——

Slots ​

NameDescription
defaultCommand item label content (default slot text); direct child <oas-command-item> elements recursively become the page sub-page

CommandItem fields:

FieldDescriptionType
labelDisplay textstring
valueSelected value (oas-select detail.value)string
keywordsSearch keywords (optional), matched in addition to the labelstring[]
groupGroup name (optional); same-group items render a group titlestring
disabledDisables the item (not selectable via Enter/click, skipped by arrow keys)boolean
iconItem icon (SVG path d string or full <svg> markup; host data, treated as trusted content)string
shortcutShortcut annotation (rendered as a right-aligned kbd), e.g. "meta+p" / "ctrl+shift+s"string
descriptionSubtitle description textstring
pageSub-page commands (nested page): selecting enters the sub-page; Esc / Backspace with an empty query pops backCommandItem[]
viewView slot name: selecting enters <slot name="view-{view}"> (in-panel view)string
forceMountIgnores filtering and always shows (create-style entry)boolean
separatorRenders as a separator row (not navigable / not selectable)boolean

Keyboard: ↑/↓ move the highlight (skipping disabled items), Enter executes and closes (multi-select toggles checks), Esc closes (sub-pages pop first), Backspace with an empty query pops a sub-page, Tab cycles between the search input and the options (focus trap); on open the search input is focused, and on close focus returns to the source element. The global shortcut defaults to ⌘K / Ctrl+K (hotkey is configurable or can be disabled).

Released under the MIT OR Apache-2.0 License.