Skip to content
llms.txt
llms.txt

Query and results

<floating-query> supplies the input-side behavior for a searchable floating surface. Pair it with <floating-results> and its result parts to render loading, empty, error, status, and pagination states without repeating the ARIA and keyboard wiring.

The query does not own your selected value or form submission. Your application decides what an activated result means.

For contenteditable, Tiptap slash commands, and mentions, use the editor Query guide. It explains the external input contract added in 0.11.0, direct keyboard handling, and the editor-side guards that remain necessary.

<floating-root placement="bottom-start" interactions="click dismiss">
<floating-query id="destination-query">
<floating-reference>
<input
aria-label="Destination"
autocomplete="off"
placeholder="Search destinations"
/>
</floating-reference>
<template slot="content">
<floating-results>
<floating-results-status></floating-results-status>
<floating-results-empty>No matches</floating-results-empty>
<floating-results-item data-search-text="name">
Result
</floating-results-item>
<floating-results-more>Load more</floating-results-more>
</floating-results>
</template>
</floating-query>
</floating-root>

The component establishes the combobox/listbox/option relationship and active descendant behavior. Inside a native dialog it automatically uses dialog semantics. Use semantics="dialog" for a non-native dialog surface, or semantics="none" when your application owns the ARIA contract.

API Contract
query.configure({search, getItemLabel, getItemKey?, semantics?, externalInput?}) Accept SearchOptions or an existing SearchController; the element owns a controller created from options
query.externalInput = editorElement Property-only external focus target; the query text and editor DOM remain application-owned
input-selector Discover a native HTMLInputElement inside the Query; this is not a contenteditable selector
query.setQuery(text, event?) Programmatic native query update; it can write the bound native input value
query.setExternalQuery(text, {composing?, event?}) Supply editor-extracted text and composition state without replacing the editor’s content
query.handleKeyDown(event) Handle direct list navigation and Enter; return false for a closed/disconnected Query, composition, or an already handled event
query.controller / query.search Access the shared Query and search controllers once the component is ready
queryactivate Emit {item, sourceEvent?} when an item activates; the application validates and applies the action
querystatechange Emit {snapshot} with active index and search state for a custom renderer

Await query.updateComplete after configuration before using its controller. Keep the Query inside floating-list navigation for ordered navigation. An external Query enables the list’s manual keyboard mode while bound, so the application passes events from its editor keyboard hook.

The adapter and controller handlers have different scopes:

Handler Handles
Web Components query.handleKeyDown(event) Ancestor list navigation followed by Query Enter activation
floating-list.handleKeyDown(event) List navigation only; use manual-keyboard to disable automatic keydown handling
Web QueryController.handleKeyDown(event) and Vue useQuery().handleKeyDown(event) Enter activation only
ListNavigationPlugin.handleKeyDown(event, source?) Navigation from a connected listNavigation() or query.navigationPlugin() plugin

List navigation defaults to source: 'floating' for a direct call, including Home/End option navigation. Use 'reference' when text-input Home/End should retain its native meaning. keyboardHandling: 'manual' leaves keyboard event ordering to the editor and retains pointer/open-state behavior.

Query’s Enter handler leaves event.isComposing, event.keyCode === 229, search.composing, and already prevented events unhandled. Escape, Tab, modified-key policy, and document-range validity remain application-owned. The keyboard guard does not validate arbitrary programmatic or pointer activation callbacks. See editor queries for the complete ownership and migration guidance.

bindExternalInput(element) on the Web controller or Vue composable installs no native input, composition, or keyboard listeners. Its cleanup restores aria-busy, data-loading, and the active-descendant attribute it owns under combobox semantics. With semantics: 'none', external binding leaves the editor-owned active descendant alone. Do not bind Vue inputProps to that same external editor.

For Web Components, the root’s real reference supplies interactions and ARIA; use setPositionReference() separately for caret geometry. Removing or rebinding the root restores the original reference attributes, and ancestor reconnection re-registers Query, List, and ListItem behavior.

Prefer the floating-results-* parts for result phases:

  • floating-results-status exposes status text.
  • floating-results-idle, floating-results-loading, floating-results-error, and floating-results-empty render phase-specific content.
  • floating-results-item binds an option.
  • floating-results-more provides a load-more action.

<floating-search> remains registered as a deprecated compatibility alias for <floating-results>. Use the floating-results-* names in new markup.

The components are headless and do not add a scrollbar policy. Give the results surface a viewport-safe max-height and make the result list the scroll container when there are many results:

.results-panel {
width: min(28rem, calc(100vw - 2rem));
max-height: min(26rem, calc(100dvh - 2rem));
overflow: hidden;
}
.results-list {
max-height: inherit;
overflow: auto;
overscroll-behavior: contain;
}

Keep this policy on the result surface or list, not as a package-wide default; menus, tooltips, dialogs, and toast stacks need different overflow behavior.