Combobox
Allow users to choose from a list of options with search filtering functionality.
<script lang="ts">
import { Combobox, ComboboxContent, ComboboxInput, ComboboxOption } from 'lithesome';
import { cn } from '$site/utils.js';
import { CheckIcon, ChevronDownIcon } from 'lucide-svelte';
import { scale } from 'svelte/transition';
const options = [
{ value: 'aang', label: 'Avatar Aang' },
{ value: 'zuko', label: 'Firelord Zuko' },
{ value: 'sokka', label: 'Councilman Sokka' },
{ value: 'katara', label: 'Katara', disabled: true },
{ value: 'toph', label: 'Greatest Earthbender Alive' },
{ value: 'iroh', label: 'Uncle Iroh' },
{ value: 'azula', label: 'Firebending Prodigy' }
];
let value = $state('aang');
let query = $state('Avatar Aang');
let touched = $state(false);
let label = $state('');
const filteredOptions = $derived(
touched
? options.filter(
(item) =>
item.value.toLowerCase().includes(query.toLowerCase()) ||
item.label.toLowerCase().includes(query.toLowerCase())
)
: options
);
</script>
<Combobox
bind:value
bind:touched
bind:label
class="w-[350px]"
onChange={(payload) => {
if (payload?.label) query = payload.label;
}}
>
{#snippet children({ visible })}
<div class="relative flex items-center">
<ComboboxInput
bind:value={query}
class={cn(
'w-full rounded-md border py-3 pl-3.5 pr-5 text-sm font-semibold',
'border-neutral-300 bg-black/5 dark:border-white/10 dark:bg-white/5',
'hover:border-neutral-400 dark:hover:border-white/20',
'focusOutline'
)}
/>
<ChevronDownIcon class={cn('absolute right-4 size-4', visible ? 'rotate-180' : '')} />
</div>
<ComboboxContent
class={cn(
'z-10 origin-top translate-y-1 rounded-xl border p-2 shadow-xl backdrop-blur',
'border-neutral-300 bg-white shadow-neutral-200',
'dark:border-neutral-700 dark:bg-neutral-900 dark:shadow-[#111]'
)}
transition={[scale, { start: 0.8, duration: 150 }]}
sameWidth
>
{#each filteredOptions as { value, label, disabled } (value)}
<ComboboxOption
{value}
{disabled}
class={({ hovered, selected }) =>
cn(
disabled ? 'text-black/40 line-through dark:text-white/30' : '',
hovered ? 'bg-black/10 text-black dark:bg-white/10 dark:text-white' : '',
selected ? 'text-teal-500' : '',
'flex w-full items-center gap-2 truncate rounded-md px-3.5 py-2.5 text-sm'
)}
>
{#snippet children({ selected })}
<div class="flex-1 text-left">{label}</div>
{#if selected}
<CheckIcon class="size-4" />
{/if}
{/snippet}
</ComboboxOption>
{:else}
<span class="block p-3 text-sm text-neutral-400 dark:text-neutral-500">No results found...</span>
{/each}
</ComboboxContent>
{/snippet}
</Combobox>
<span class="pointer-events-none absolute bottom-2 right-2 select-none text-xs opacity-40">Value: {value}</span>
API Reference
Combobox
Prop | Type | Description |
---|---|---|
value * | T Default: —— | The selected value of the available options. |
touched | boolean Default: —— | True if the input has been given any input. |
label | string Default: —— | The label of the selected value(s). This is blank if the value is set to an array. |
use | Array Default: [] | Any svelte action to be applied to the underlying element. View use api for more info. |
self | HTMLDivElement Default: —— | The underlying html element that you can use to bind to. |
Child prop | Type | Description |
---|---|---|
visible | boolean | Whether or not the content component is visible or not. |
Data attribute | Value | Description |
---|---|---|
data-combobox | '' | The base data attribute, can be used for styling. |
data-state | 'opened' | 'closed' | Whether or not the content component is visible or not. |
Event | Param & Return | Description |
---|---|---|
onChange | (payload: {value: string, label: string}) void | Fires any time a new option is selected. |
ComboboxInput
This component is a child of Combobox
Prop | Type | Description |
---|---|---|
value * | string Default: —— | The value of the underlying input element. |
disabled | boolean Default: false | Disables the input. |
use | Array Default: [] | Any svelte action to be applied to the underlying element. View use api for more info. |
self | HTMLInputElement Default: —— | The underlying html element that you can use to bind to. |
Data attribute | Value | Description |
---|---|---|
data-comboboxinput | '' | The base data attribute, can be used for styling. |
Event | Param & Return | Description |
---|---|---|
onClick | (e: MouseEvent) void | |
onFocus | (e: FocusEvent) void | |
onKeydown | (e: KeyboardEvent) void |
ConboboxContent
This component is a child of Combobox
Prop | Type | Description |
---|---|---|
placement | Placement Default: bottom | The FloatingUI placement string. |
constrainViewport | boolean Default: false | Keeps the content from ever growing outside of the viewport. |
sameWidth | boolean Default: false | Makes the content the same width as the trigger. |
portalTarget | strng | HTMLElement Default: 'body' | The target position for the content to be mounted. |
use | Array Default: [] | Any svelte action to be applied to the underlying element. View use api for more info. |
self | HTMLDivElement Default: —— | The underlying html element that you can use to bind to. |
transition | Transition Default: undefined | The svelte transition you wish to use. View transition api for more info. |
Child prop | Type | Description |
---|---|---|
visible | boolean | Whether or not the content component is visible or not. |
Data attribute | Value | Description |
---|---|---|
data-conboboxcontent | '' | The base data attribute, can be used for styling. |
ComboboxArrow
This component is a child of ComboboxContent
Prop | Type | Description |
---|---|---|
self | HTMLDivElement Default: —— | The underlying html element that you can use to bind to. |
use | Array Default: [] | Any svelte action to be applied to the underlying element. View use api for more info. |
Data attribute | Value | Description |
---|---|---|
data-comboboxarrow | '' | The base data attribute, can be used for styling. |
data-side | 'top' | 'right' | 'bottom' | 'left' |
ComboboxOption
This component is a child of ConboboxContent
Prop | Type | Description |
---|---|---|
value * | JsonValue Default: —— | The value of the option. |
label | string Default: —— | The label of the option. If this is not passed, the text content of the option will be used. |
disabled | boolean Default: false | Disables the option, disallowing clicking and keyboard navigation. |
use | Array Default: [] | Any svelte action to be applied to the underlying element. View use api for more info. |
self | HTMLButtonElement Default: —— | The underlying html element that you can use to bind to. |
Child prop | Type | Description |
---|---|---|
hovered | boolean | Whether or not the option is currently being hovered, either by mouse or keyboard. |
selected | boolean | If the option is the selected value. |
Data attribute | Value | Description |
---|---|---|
data-comboboxoption | '' | The base data attribute, can be used for styling. |
data-hovered |
| This is only applied if the option is hovered. |
data-selected |
| This is only applied if the option is selected. |
Event | Param & Return | Description |
---|---|---|
onClick | (e: MouseEvent) void | |
onMouseeneter | (e: MouseEvent) void | |
onFocus | (e: FocusEvent) void |
Basic Example
<script>
import { Combobox, ComboboxInput, ComboboxContent, ComboboxOption, ComboboxArrow } from 'lithesome';
</script>
<Combobox>
<ComboboxInput />
<ComboboxContent>
<ComboboxArrow />
<ComboboxOption />
</ComboboxContent>
</Combobox>
Multiple selected options
If you need to have a multiple selected at once, change the value
prop to any form of an array.
The component will then detect the array and append the selected options.
<script>
import { Combobox, ComboboxInput, ComboboxContent, ComboboxOption } from 'lithesome';
let value = $state([]);
let query = $state('');
</script>
<Combobox bind:value>
<ComboboxInput bind:query />
<ComboboxContent>
<ComboboxOption value="hamburger" />
<ComboboxOption value="cheeseburger" />
</ComboboxContent>
</Combobox>