Feed commands through the items property: plain items, { type: 'group', label, items } groups and { type: 'separator' } dividers. Each item takes a value, label, optional description, icon, shortcut, keywords and disabled. A description is a secondary line after the label, in the command-palette-item-description part, and becomes the item’s accessible description. Typing filters and ranks the list; arrow keys, Home and End move the active command, and Enter runs it. Disabled matches remain visible but cannot execute. Pointer hover never scrolls or executes. IME suppresses navigation/activation until composition ends.
Running a command fires a cancelable tp-execute event with { commandId }. The palette does not select, remember or close anything on its own, so your handler decides what happens next. Shortcuts are informative only: the palette never installs global key bindings.
In a dialog
Leave inline off and put a Button in the trigger slot to present the palette in a modal Dialog, with the Dialog’s focus trap, Escape and outside dismissal. For a quick list, native option children work instead of items. showCloseControl defaults false for the palette, but a Dialog requires a reachable named close action: the corner Close remains visible unless you supply another, such as a Button in slot="close".
Modal palette
Default density
Compact density
Restart from the initial state
Show the copyable source
Here the page owns the shortcut: a keydown listener opens the palette with setOpen(), and the tp-execute handler closes it after navigating.
Empty results
When nothing matches, the palette shows “No results found.” Replace it with the empty slot to point people somewhere useful.
Custom empty state
Default density
Compact density
Restart from the initial state
Show the copyable source
No teammates match. Invite them from Team settings.
No teammates match. Invite them from Team settings.
<tp-command-palette inline label="Teammates" default-query="priya" placeholder="Search teammates…" style="max-inline-size:28rem;margin-inline:auto">
<option value="maya">Maya Patel</option>
<option value="jordan">Jordan Lee</option>
<option value="ana">Ana Ruiz</option>
<span slot="empty">No teammates match. Invite them from Team settings.</span>
</tp-command-palette>
default-query sets the starting search text. Use query with onQueryChange when the application needs to own the query, for example to sync it with the URL.
Server results
When a server does the searching, set the shouldFilter property to false so the palette shows items exactly as supplied, then replace items whenever the query changes. tp-query-change reports each proposed query in detail.value.
Server-filtered list
Default density
Compact density
Restart from the initial state
Show the copyable source
<tp-command-palette id="command-palette-server" inline label="Search docs" placeholder="Search the documentation…" style="max-inline-size:28rem;margin-inline:auto"></tp-command-palette>
<script type="module">
const pages = [
'Installing the CLI', 'Inviting teammates', 'Managing billing', 'Exporting data as CSV',
'Connecting GitHub', 'Setting up single sign-on', 'Rotating API keys', 'Webhooks reference',
'Audit log retention', 'Deleting a workspace',
];
await customElements.whenDefined('tp-command-palette');
const palette = document.getElementById('command-palette-server');
palette.shouldFilter = false;
const show = (titles) => {
palette.items = titles.map((title) => ({ value: title.toLowerCase().replaceAll(' ', '-'), label: title }));
};
show(pages.slice(0, 4));
let request = 0;
palette.addEventListener('tp-query-change', (event) => {
const query = event.detail.value.trim().toLowerCase();
const current = ++request;
// Stands in for fetch('/api/search?q=' + query); stale responses are ignored.
setTimeout(() => {
if (current !== request) return;
show(query ? pages.filter((page) => page.toLowerCase().includes(query)) : pages.slice(0, 4));
}, 300);
});
</script>
To keep local filtering but change the ranking, assign a filter function instead: return false to hide an item or a number to sort by, lowest first.
Command list with an external input
tp-command-list is the palette’s list member. Used on its own, inline false presents its list and empty state in a popup in the top layer at anchor (an element or a virtual anchor, property only) with placement (default block-end start), shown while its open is true. matching, source, searchDelay, highlightMatches and matchFields work as in Select.
Set queryOwner (property only, default null) to an editing element that keeps focus, such as an editor in which a typed trigger character opens suggestions. The list then renders no input: the owner supplies the query through inputValue, controls open, closes the list when onOpenChange proposes closing (Escape or a press outside both the owner and the list), and forwards its keys:
handleQueryKey(event) applies Arrow Up, Arrow Down, Home and End to the highlight and returns whether it consumed the key; activateHighlighted(event) executes the highlighted item for Enter or Tab and returns false when nothing is highlighted, so the key reaches the owner. The list never moves focus from the owner, also on a pointer press on an item, and closing it aborts a pending source call. activeDescendantReference() returns the list element, which forwards aria-activedescendant and aria-controls references to the highlighted item, or null where such references cannot cross into its shadow root; announce the highlighted item instead in that case.
API Reference
Every element also accepts motion-policy.
Attributes
AttributePropertyTypeDefaultDescriptioninlineinlinebooleanfalseShows the palette in the page instead of in a dialog opened by its trigger.queryquerystring—The search text; set it from the start to own it, writing back each accepted proposal.default-querydefaultQuerystring | undefined—The search text at first, when query is not set.should-filtershouldFilterbooleantrueWhether the palette filters and ranks the items itself; set the property to false when a server returns the matches.loop-navigationloopNavigationbooleanfalseWhether the arrow keys wrap from the last command to the first.placeholderplaceholderstring'Type a command or search…'Placeholder text of the search field.
Properties
PropertyTypeDefaultDescriptionitemsreadonly CommandEntry[] | undefined—The commands: items with a value, label and optional description, icon, shortcut and keywords, groups of items and separators.defaultValueunknown—The command active at first, when value is not set.filterCommandFilter | null | undefined—Ranks the items for a query: return false to hide an item, or a number to sort by, lowest first.onQueryChange((event: TpValueChangeEvent<string>) => void) | undefined—Called with each search-text proposal before tp-query-change is dispatched.onValueChange((event: TpValueChangeEvent<unknown>) => void) | undefined—Called with each active-command proposal before tp-value-change is dispatched.onExecute((event: CustomEvent<{ commandId: unknown; sourceEvent: Event }>) => void) | undefined—Called when a command is run, before tp-execute is dispatched.valueunknown—The value of the active command; set it from the start to own it.inputElementread-onlyHTMLInputElement | null—The search field, for focusing it from code.noExecutableMatchread-onlyboolean—Whether the query matches no command that can run.
Events
EventTypeDescriptiontp-executeCustomEvent<{ commandId: unknown; sourceEvent: Event; }>Cancelable, when a command is run; detail.commandId is its value.tp-query-changeTpValueChangeEvent<string>Cancelable proposal of the search text; detail.value.tp-value-changeTpValueChangeEvent<unknown>Proposal of the active command's value as the highlight moves.tp-open-changeTpSurfaceOpenChangeEventCancelable proposal to open or close, before the dialog changes; detail.value is the proposed state and detail.reason what asked (trigger-press, close-action, escape-key, outside-press, imperative-action and others). On a close, detail.preventUnmountOnClose() keeps the content mounted.tp-open-change-completeCustomEvent<{ open: boolean; }>After the dialog finishes opening or closing; detail.open.
Slots
SlotDescriptiondefaultNative option elements, as a quick list in place of items.emptyShown when no command matches the query.triggerThe control that opens the dialog, usually a Button.titleThe title, in place of label.descriptionThe description, in place of description.footerFooter content with no close behavior, such as a secondary action.closeAn action that closes the dialog when pressed, after its own handlers.