Skip to content

Autocomplete

Autocomplete is an editable text input that filters a generic Items<T> collection as you type. Focus stays in the Input; the highlighted suggestion is tracked with aria-activedescendant rather than moving DOM focus. It can be controlled (@bind-Value, @bind-Open) or uncontrolled (DefaultOpen), and the Popup self-portals to document.body, anchored to the Input.

@using Navius.Primitives.Components.Autocomplete
<NaviusAutocomplete>
<NaviusAutocompleteInput />
<NaviusAutocompletePortal>
<NaviusAutocompletePositioner>
<NaviusAutocompletePopup>
<NaviusAutocompleteList />
<NaviusAutocompleteEmpty />
</NaviusAutocompletePopup>
</NaviusAutocompletePositioner>
</NaviusAutocompletePortal>
</NaviusAutocomplete>

Owns the filtered collection, the open state and the committed value. Cascades the Autocomplete context. Renders no DOM of its own.

Prop Type Default Description
Items IReadOnlyList<TItem> [] The full item set to filter.
Value string? - The input text, which is also the committed value. Pair with ValueChanged (@bind-Value).
ValueChanged EventCallback<string?> - Fires when the controlled Value should change.
Open bool false Controlled open state. Pair with OpenChanged (@bind-Open).
OpenChanged EventCallback<bool> - Fires when the controlled Open value should change.
DefaultOpen bool false Initial open state when used uncontrolled.
ItemToString Func<TItem, string>? - Projects an item to its display/match text. Defaults to x?.ToString().
Filter Func<TItem, string, bool>? - Predicate (item, query) => keep. Defaults to a case-insensitive Contains; an empty query shows all items.
ItemTemplate RenderFragment<TItem>? - Per-row template. When null, the List renders the item’s text in a default Item.
Dir string? - Reading direction, "ltr" or "rtl". Cascaded to the popup.
ChildContent RenderFragment? - The autocomplete’s parts.

The editable text field (role="combobox"). Typing filters the list and updates the value. Also doubles as the trigger/anchor. Renders an input.

Prop Type Default Description
Placeholder string? - Placeholder text. Not an accessible name.
Attributes IDictionary<string, object>? - Forwarded to the rendered input.

Optional wrapper that hoists the Popup to document.body (or a Container). Renders no DOM of its own.

Prop Type Default Description
Container string? - CSS selector for the mount target. null uses document.body.
KeepMounted bool false Keeps the popup mounted while closed, for exit animations.
ChildContent RenderFragment? - The Positioner and Popup.

Owns placement (side / align / offsets / collision). A flag-setter; the Popup renders the positioning element the engine anchors and writes data-side / data-align + --anchor-* onto.

Prop Type Default Description
Side string "bottom" Preferred side: top, right, bottom, left.
Align string "start" Alignment along the side: start, center, end.
SideOffset double 0 Distance in px from the anchor along the side.
AlignOffset double 0 Offset in px along the alignment axis.
Flip bool true Flip to the opposite side on collision.
AvoidCollisions bool true Avoid collisions with the viewport boundary.
ChildContent RenderFragment? - The Popup.

The floating surface that wraps the listbox, self-portaled and anchored to the Input. The listbox role and id live on the List part, not here; the Popup carries the discrete presence attributes. Runs with focus trapping and open auto-focus both disabled: focus never leaves the Input (virtual focus). Renders a div.

Prop Type Default Description
ChildContent RenderFragment? - The List and Empty parts.
Attributes IDictionary<string, object>? - Forwarded to the rendered div.
Part Attribute Description
Popup data-open Present while the popup is open.
Popup data-closed Present while closing (through the exit transition).
Popup data-starting-style Present at the start of the open transition.
Popup data-ending-style Present at the start of the close transition.
Popup data-side "top" | "right" | "bottom" | "left".
Popup data-align "start" | "center" | "end".
Popup data-empty Present when the filtered collection has no matches.
List data-empty Present when the filtered collection has no matches.
Item data-highlighted Present when the item is the active (aria-activedescendant) option.
Item data-selected Present when the item matches the committed value.

Renders one Item per filtered entry using the Root’s ItemTemplate. Owns role="listbox" and the id the Input’s aria-controls points at. Renders a ul.

Prop Type Default Description
Attributes IDictionary<string, object>? - Forwarded to the rendered ul.

A single suggestion. Highlighted by keyboard/pointer via virtual focus (no tabindex of its own); selecting it commits its value. Renders an li with role="option".

Prop Type Default Description
ChildContent RenderFragment? - Item content.
Attributes IDictionary<string, object>? - Forwarded to the rendered li.

Shown inside the Popup, alongside the List, only while open and the filtered collection has no matches. Renders a div.

Prop Type Default Description
ChildContent RenderFragment? - Empty-state content.
Attributes IDictionary<string, object>? - Forwarded to the rendered div.
Key Behavior
ArrowDown Opens the list if closed; otherwise moves the highlight to the next option.
ArrowUp Moves the highlight to the previous option; if closed, opens the list and highlights the last option.
Home / PageUp Moves the highlight to the first option (only while open).
End / PageDown Moves the highlight to the last option (only while open).
Enter Selects the highlighted option and closes the list.
Escape Closes the list, keeping focus in the input.
Tab Closes the list and moves focus to the next element.

Implements the combobox WAI-ARIA APG pattern (editable, list-autocomplete). The Input wires role="combobox", aria-expanded, aria-controls pointing at the List’s id, and aria-autocomplete="list". The highlighted option is tracked with aria-activedescendant on the Input so DOM focus never leaves it; there is no roving tabindex. The List wires role="listbox"; each Item wires role="option" and aria-selected. The Input ships without an accessible name of its own, so pair it with a label or supply aria-label. This mechanism is covered by the Playwright browser test suite and gated by axe-core in CI.