Listbox
The Listbox
component is a fully headless and accessible UI primitive for selecting from a list of options. It's inspired by HeadlessUI Listbox, but built natively for Angular.
It is useful for building custom select dropdowns, option pickers, and choice selectors — while giving you complete control over markup and styling.
When to use it
Use Listbox
when you need a select component that is:
- Fully customizable in layout and appearance
- Accessible with screen readers and keyboard navigation
- Controlled via Angular
templateRefs
orDependency Injection (DI)
- Compatible with reactive or template-driven forms
Common use cases:
- Custom select dropdowns
- Option pickers
- Choice selectors
- Filter controls
Anatomy
A complete Listbox
is composed of four components:
Listbox
- The provider and wrapper. Manages internal selection state.ListboxButton
- The trigger that toggles the options open or closed.ListboxOptions
- The container for all selectable options.ListboxOption
- Individual selectable option items.
All four components are standalone and composable.
Features
- ✅ Accessible by default (ARIA attributes)
- ✅ Tailwind-friendly (no styles imposed)
- ✅ Multiple selectors supported:
<Listbox>
<div ngxListbox>
<ngx-headlessui-listbox>
- ✅ Works with both
#templateRefs
and Angular DI (ListboxContextService
) - ✅ Supports multiple instances per page
Installation
Listbox
ships as part of the @ngx-headless/ui
by default. Install if you haven't already.
npm install @ngx-headless/ui
Import the components directly:
import {
ListboxComponent,
ListboxButtonComponent,
ListboxOptionsComponent,
ListboxOptionComponent,
} from "@ngx-headless/ui";
Usage Examples
Template Reference — Single Listbox
<Listbox #l="ngxListbox" [(modelValue)]="selectedValue">
<ListboxButton [class.active]="l.isOpen()">{{ selectedValue?.name || 'Select option' }}</ListboxButton>
<ListboxOptions *ngIf="l.isOpen()">
<ListboxOption *ngFor="let option of options" [value]="option">
{{ option.name }}
</ListboxOption>
</ListboxOptions>
</Listbox>
Template Reference — Multiple Listboxes (ngFor)
<Listbox *ngFor="let filter of filters" #l="ngxListbox" [(modelValue)]="filter.value">
<ListboxButton>{{ filter.value?.name || filter.placeholder }}</ListboxButton>
<ListboxOptions *ngIf="l.isOpen()">
<ListboxOption *ngFor="let option of filter.options" [value]="option">
{{ option.name }}
</ListboxOption>
</ListboxOptions>
</Listbox>
Accessibility
This component handles
aria-expanded
on theListboxButton
aria-haspopup="listbox"
on theListboxButton
role="listbox"
on theListbox
andListboxOptions
role="option"
on eachListboxOption
aria-selected
on the selectedListboxOption
- Keyboard interaction: toggle on Enter / Space, selection with arrow keys
- Screen reader compatibility for selection state
Animations
ListboxOptions
can be animated freely using Angular's built-in animation system.
Example
Add an animation trigger to your component:
import { trigger, transition, style, animate } from "@angular/animations";
@Component({
animations: [
trigger("slideDown", [
transition(":enter", [
style({ opacity: 0, transform: "translateY(-10px)" }),
animate("150ms ease-out", style({ opacity: 1, transform: "translateY(0)" })),
]),
transition(":leave", [
animate("100ms ease-in", style({ opacity: 0, transform: "translateY(-10px)" })),
]),
]),
],
})
export class ExampleComponent {}
Use it in the template with *ngIf
:
<ListboxOptions *ngIf="listbox.isOpen()" @slideDown>
Options go here.
</ListboxOptions>
Component API
ListboxComponent
Input | Type | Description |
---|---|---|
modelValue | any | Currently selected value |
class | string | Class for styling wrapper |
Output | Type | Description |
---|---|---|
modelValueChange | EventEmitter<any> | Emits when selection changes |
Method | Description |
---|---|
isOpen() | Returns true if options open |
toggle() | Toggles options open/close |
open() | Opens options |
close() | Closes options |
selectValue() | Selects a specific value |
ListboxButtonComponent
- Automatically binds
aria-expanded
andaria-haspopup
- Toggles options on click and keyboard interaction
ListboxOptionsComponent
- Visible only when
Listbox
is open - Can be styled or animated freely
hidden
attribute used for visibility
ListboxOptionComponent
Input | Type | Description |
---|---|---|
value | any | Value for this option |
disabled | boolean | Prevents selection if true |
class | string | Class applied to the option |
Output | Type | Description |
---|---|---|
selected | EventEmitter<any> | Emits when option is selected |