- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
<script lang="ts">
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-svelte';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
let items = $state(data);
const collection = $derived(
useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
}),
);
const onOpenChange = () => {
items = data;
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
items = filtered;
} else {
items = data;
}
};
</script>
<Combobox class="w-full max-w-md" placeholder="Search..." {collection} {onOpenChange} {onInputValueChange}>
<Combobox.Label>Label</Combobox.Label>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner class="z-[1]!">
<Combobox.Content>
{#each items as item (item.value)}
<Combobox.Item {item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
{/each}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-react';
import { useState } from 'react';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
export default function Default() {
const [items, setItems] = useState(data);
const collection = useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
});
const onOpenChange = () => {
setItems(data);
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
setItems(filtered);
} else {
setItems(data);
}
};
return (
<Combobox
className="w-full max-w-md"
placeholder="Search..."
collection={collection}
onOpenChange={onOpenChange}
onInputValueChange={onInputValueChange}
>
<Combobox.Label>Label</Combobox.Label>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner className="z-[1]!">
<Combobox.Content>
{items.map((item) => (
<Combobox.Item key={item.value} item={item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
))}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
);
}
Groups
Create labelled groups for your items.
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
<script lang="ts">
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-svelte';
const data = [
{ label: 'Apple', value: 'apple', type: 'Fruits' },
{ label: 'Banana', value: 'banana', type: 'Fruits' },
{ label: 'Orange', value: 'orange', type: 'Fruits' },
{ label: 'Carrot', value: 'carrot', type: 'Vegetables' },
{ label: 'Broccoli', value: 'broccoli', type: 'Vegetables' },
{ label: 'Spinach', value: 'spinach', type: 'Vegetables' },
];
let items = $state(data);
const collection = $derived(
useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
groupBy: (item) => item.type,
}),
);
const onOpenChange = () => {
items = data;
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
items = filtered;
} else {
items = data;
}
};
</script>
<Combobox class="w-full max-w-md" placeholder="Search..." {collection} {onOpenChange} {onInputValueChange}>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner class="z-[1]!">
<Combobox.Content>
{#each collection.group() as [type, items] (type)}
<Combobox.ItemGroup>
<Combobox.ItemGroupLabel>{type}</Combobox.ItemGroupLabel>
{#each items as item (item.value)}
<Combobox.Item {item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
{/each}
</Combobox.ItemGroup>
{/each}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
'use client';
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-react';
import { useState } from 'react';
const data = [
{ label: 'Apple', value: 'apple', type: 'Fruits' },
{ label: 'Banana', value: 'banana', type: 'Fruits' },
{ label: 'Orange', value: 'orange', type: 'Fruits' },
{ label: 'Carrot', value: 'carrot', type: 'Vegetables' },
{ label: 'Broccoli', value: 'broccoli', type: 'Vegetables' },
{ label: 'Spinach', value: 'spinach', type: 'Vegetables' },
];
export default function Group() {
const [items, setItems] = useState(data);
const collection = useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
groupBy: (item) => item.type,
});
const onOpenChange = () => {
setItems(data);
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
setItems(filtered);
} else {
setItems(data);
}
};
return (
<Combobox
className="w-full max-w-md"
placeholder="Search..."
collection={collection}
onOpenChange={onOpenChange}
onInputValueChange={onInputValueChange}
>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner className="z-[1]!">
<Combobox.Content>
{collection.group().map(([type, items]) => (
<Combobox.ItemGroup key={type}>
<Combobox.ItemGroupLabel>{type}</Combobox.ItemGroupLabel>
{items.map((item) => (
<Combobox.Item key={item.value} item={item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
))}
</Combobox.ItemGroup>
))}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
);
}
Auto Highlight
Search for any option, then tap Enter on your keyboard to automatically select it.
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
<script lang="ts">
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-svelte';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
let items = $state(data);
const collection = $derived(
useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
}),
);
const onOpenChange = () => {
items = data;
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
items = filtered;
} else {
items = data;
}
};
</script>
<Combobox class="w-full max-w-md" placeholder="Search..." {collection} {onOpenChange} {onInputValueChange} inputBehavior="autohighlight">
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner class="z-[1]!">
<Combobox.Content>
{#each items as item (item.value)}
<Combobox.Item {item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
{/each}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-react';
import { useState } from 'react';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
export default function AutoHighlight() {
const [items, setItems] = useState(data);
const collection = useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
});
const onOpenChange = () => {
setItems(data);
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
setItems(filtered);
} else {
setItems(data);
}
};
return (
<Combobox
className="w-full max-w-md"
placeholder="Search..."
collection={collection}
onOpenChange={onOpenChange}
onInputValueChange={onInputValueChange}
inputBehavior="autohighlight"
>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner className="z-[1]!">
<Combobox.Content>
{items.map((item) => (
<Combobox.Item key={item.value} item={item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
))}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
);
}
Multiple
To maintain filtering functionality and improve clarity for users, we recommend displaying each selected value outside the perimeter of the Combobox component.
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
<script lang="ts">
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-svelte';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
let value: string[] = $state([]);
let items = $state(data);
const collection = $derived(
useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
}),
);
const onOpenChange = () => {
items = data;
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
items = filtered;
} else {
items = data;
}
};
const onValueChange: ComboboxRootProps['onValueChange'] = (event) => {
value = event.value;
};
</script>
<div class="grid gap-2 w-full max-w-md">
<Combobox placeholder="Search..." {collection} {onOpenChange} {onInputValueChange} {value} {onValueChange} multiple>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner class="z-[1]!">
<Combobox.Content>
{#each items as item (item.value)}
<Combobox.Item {item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
{/each}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
<div class="flex flex-wrap gap-2">
{#each value as item (item)}
<span class="badge preset-filled">
{item}
</span>
{/each}
</div>
</div>
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-react';
import { useState } from 'react';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
export default function Default() {
const [value, setValue] = useState<string[]>([]);
const [items, setItems] = useState(data);
const collection = useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
});
const onOpenChange = () => {
setItems(data);
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
setItems(filtered);
} else {
setItems(data);
}
};
const onValueChange: ComboboxRootProps['onValueChange'] = (event) => {
setValue(event.value);
};
return (
<div className="grid gap-2 w-full max-w-md">
<Combobox
placeholder="Search..."
collection={collection}
onOpenChange={onOpenChange}
onInputValueChange={onInputValueChange}
multiple={true}
value={value}
onValueChange={onValueChange}
>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner className="z-[1]!">
<Combobox.Content>
{items.map((item) => (
<Combobox.Item key={item.value} item={item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
))}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
<div className="flex flex-wrap gap-2">
{value.map((item) => (
<span key={item} className="badge preset-filled">
{item}
</span>
))}
</div>
</div>
);
}
Disabled Item
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
<script lang="ts">
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-svelte';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
let items = $state(data);
const collection = $derived(
useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
isItemDisabled: (item) => item.value === 'banana',
}),
);
const onOpenChange = () => {
items = data;
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
items = filtered;
} else {
items = data;
}
};
</script>
<Combobox class="w-full max-w-md" placeholder="Search..." {collection} {onOpenChange} {onInputValueChange}>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner class="z-[1]!">
<Combobox.Content>
{#each items as item (item.value)}
<Combobox.Item {item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
{/each}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-react';
import { useState } from 'react';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
export default function Default() {
const [items, setItems] = useState(data);
const collection = useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
isItemDisabled: (item) => item.value === 'banana',
});
const onOpenChange = () => {
setItems(data);
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
setItems(filtered);
} else {
setItems(data);
}
};
return (
<Combobox
className="w-full max-w-md"
placeholder="Search..."
collection={collection}
onOpenChange={onOpenChange}
onInputValueChange={onInputValueChange}
>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner className="z-[1]!">
<Combobox.Content>
{items.map((item) => (
<Combobox.Item key={item.value} item={item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
))}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
);
}
Custom Filter
Try mistyping apple or banana to see the custom filter using the fuzzy search from Fuse.js in action.
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
<script lang="ts">
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-svelte';
import Fuse from 'fuse.js';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
const fuse = new Fuse(data, {
keys: ['label', 'value'],
threshold: 0.3,
});
let items = $state(data);
const collection = $derived(
useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
}),
);
const onOpenChange = () => {
items = data;
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const results = fuse.search(event.inputValue);
if (results.length > 0) {
items = results.map((result) => result.item);
} else {
items = data;
}
};
</script>
<Combobox class="w-full max-w-md" placeholder="Search..." {collection} {onOpenChange} {onInputValueChange}>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner class="z-[1]!">
<Combobox.Content>
{#each items as item (item.value)}
<Combobox.Item {item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
{/each}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-react';
import Fuse from 'fuse.js';
import { useState } from 'react';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
const fuse = new Fuse(data, {
keys: ['label', 'value'],
threshold: 0.3,
});
export default function Default() {
const [items, setItems] = useState(data);
const collection = useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
});
const onOpenChange = () => {
setItems(data);
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const results = fuse.search(event.inputValue);
if (results.length > 0) {
setItems(results.map((result) => result.item));
} else {
setItems(data);
}
};
return (
<Combobox
className="w-full max-w-md"
placeholder="Search..."
collection={collection}
onOpenChange={onOpenChange}
onInputValueChange={onInputValueChange}
>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner className="z-[1]!">
<Combobox.Content>
{items.map((item) => (
<Combobox.Item key={item.value} item={item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
))}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
);
}
Direction
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
<script lang="ts">
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-svelte';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
let items = $state(data);
const collection = $derived(
useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
}),
);
const onOpenChange = () => {
items = data;
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
items = filtered;
} else {
items = data;
}
};
</script>
<Combobox class="w-full max-w-md" placeholder="Search..." {collection} {onOpenChange} {onInputValueChange} dir="rtl">
<Combobox.Label>Label</Combobox.Label>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner class="z-[1]!">
<Combobox.Content>
{#each items as item (item.value)}
<Combobox.Item {item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
{/each}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-react';
import { useState } from 'react';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
export default function Dir() {
const [items, setItems] = useState(data);
const collection = useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
});
const onOpenChange = () => {
setItems(data);
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
setItems(filtered);
} else {
setItems(data);
}
};
return (
<Combobox
className="w-full max-w-md"
placeholder="Search..."
collection={collection}
onOpenChange={onOpenChange}
onInputValueChange={onInputValueChange}
dir="rtl"
>
<Combobox.Label>Label</Combobox.Label>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner className="z-[1]!">
<Combobox.Content>
{items.map((item) => (
<Combobox.Item key={item.value} item={item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
))}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
);
}
Guidelines
Z-Index
By default we do not take an opinionated stance regarding z-index stacking. The result is the component can sometimes be occluded beneath other elements with a higher index. The Z-Index can controlled by applying a utility class to the Positioner component part.
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
<script lang="ts">
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-svelte';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
let items = $state(data);
const collection = $derived(
useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
}),
);
const onOpenChange = () => {
items = data;
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
items = filtered;
} else {
items = data;
}
};
</script>
<Combobox class="w-full max-w-md" placeholder="Search..." {collection} {onOpenChange} {onInputValueChange}>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner class="z-50">
<Combobox.Content>
{#each items as item (item.value)}
<Combobox.Item {item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
{/each}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
- Apple
- Banana
- Orange
- Carrot
- Broccoli
- Spinach
import { Combobox, Portal, type ComboboxRootProps, useListCollection } from '@skeletonlabs/skeleton-react';
import { useState } from 'react';
const data = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
export default function ZIndex() {
const [items, setItems] = useState(data);
const collection = useListCollection({
items: items,
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
});
const onOpenChange = () => {
setItems(data);
};
const onInputValueChange: ComboboxRootProps['onInputValueChange'] = (event) => {
const filtered = data.filter((item) => item.value.toLowerCase().includes(event.inputValue.toLowerCase()));
if (filtered.length > 0) {
setItems(filtered);
} else {
setItems(data);
}
};
return (
<Combobox
className="w-full max-w-md"
placeholder="Search..."
collection={collection}
onOpenChange={onOpenChange}
onInputValueChange={onInputValueChange}
>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger />
</Combobox.Control>
<Portal>
<Combobox.Positioner className="z-50">
<Combobox.Content>
{items.map((item) => (
<Combobox.Item key={item.value} item={item}>
<Combobox.ItemText>{item.label}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
))}
</Combobox.Content>
</Combobox.Positioner>
</Portal>
</Combobox>
);
}
Max Items
We recommend no more than 500 items max. For normal usage, a few dozen will provide the best performance.
API Reference
Root
| Property | Default | Type |
|---|---|---|
open | - | boolean | undefinedThe controlled open state of the combobox |
defaultOpen | - | boolean | undefinedThe initial open state of the combobox when rendered. Use when you don't need to control the open state of the combobox. |
ids | - | Partial<{ root: string; label: string; control: string; input: string; content: string; trigger: string; clearTrigger: string; item: (id: string, index?: number | undefined) => string; positioner: string; itemGroup: (id: string | number) => string; itemGroupLabel: (id: string | number) => string; }> | undefined The ids of the elements in the combobox. Useful for composition. |
inputValue | - | string | undefinedThe controlled value of the combobox's input |
defaultInputValue | "" | string | undefinedThe initial value of the combobox's input when rendered. Use when you don't need to control the value of the combobox's input. |
name | - | string | undefinedThe `name` attribute of the combobox's input. Useful for form submission |
form | - | string | undefinedThe associate form of the combobox. |
disabled | - | boolean | undefinedWhether the combobox is disabled |
readOnly | - | boolean | undefinedWhether the combobox is readonly. This puts the combobox in a "non-editable" mode but the user can still interact with it |
invalid | - | boolean | undefinedWhether the combobox is invalid |
required | - | boolean | undefinedWhether the combobox is required |
placeholder | - | string | undefinedThe placeholder text of the combobox's input |
defaultHighlightedValue | - | string | null | undefinedThe initial highlighted value of the combobox when rendered. Use when you don't need to control the highlighted value of the combobox. |
highlightedValue | - | string | null | undefinedThe controlled highlighted value of the combobox |
value | - | string[] | undefinedThe controlled value of the combobox's selected items |
defaultValue | [] | string[] | undefinedThe initial value of the combobox's selected items when rendered. Use when you don't need to control the value of the combobox's selected items. |
inputBehavior | "none" | "autohighlight" | "autocomplete" | "none" | undefinedDefines the auto-completion behavior of the combobox. - `autohighlight`: The first focused item is highlighted as the user types - `autocomplete`: Navigating the listbox with the arrow keys selects the item and the input is updated |
selectionBehavior | "replace" | "clear" | "replace" | "preserve" | undefinedThe behavior of the combobox input when an item is selected - `replace`: The selected item string is set as the input value - `clear`: The input value is cleared - `preserve`: The input value is preserved |
autoFocus | - | boolean | undefinedWhether to autofocus the input on mount |
openOnClick | false | boolean | undefinedWhether to open the combobox popup on initial click on the input |
openOnChange | true | boolean | ((details: InputValueChangeDetails) => boolean) | undefinedWhether to show the combobox when the input value changes |
allowCustomValue | - | boolean | undefinedWhether to allow typing custom values in the input |
alwaysSubmitOnEnter | false | boolean | undefinedWhether to always submit on Enter key press, even if popup is open. Useful for single-field autocomplete forms where Enter should submit the form. |
loopFocus | true | boolean | undefinedWhether to loop the keyboard navigation through the items |
positioning | { placement: "bottom-start" } | PositioningOptions | undefinedThe positioning options to dynamically position the menu |
onInputValueChange | - | ((details: InputValueChangeDetails) => void) | undefinedFunction called when the input's value changes |
onValueChange | - | ((details: ValueChangeDetails<any>) => void) | undefinedFunction called when a new item is selected |
onHighlightChange | - | ((details: HighlightChangeDetails<any>) => void) | undefinedFunction called when an item is highlighted using the pointer or keyboard navigation. |
onSelect | - | ((details: SelectionDetails) => void) | undefinedFunction called when an item is selected |
onOpenChange | - | ((details: OpenChangeDetails) => void) | undefinedFunction called when the popup is opened |
translations | - | IntlTranslations | undefinedSpecifies the localized strings that identifies the accessibility elements and their states |
collection | - | ListCollection<any> | undefinedThe collection of items |
multiple | - | boolean | undefinedWhether to allow multiple selection. **Good to know:** When `multiple` is `true`, the `selectionBehavior` is automatically set to `clear`. It is recommended to render the selected items in a separate container. |
closeOnSelect | - | boolean | undefinedWhether to close the combobox when an item is selected. |
openOnKeyPress | true | boolean | undefinedWhether to open the combobox on arrow key press |
scrollToIndexFn | - | ((details: ScrollToIndexDetails) => void) | undefinedFunction to scroll to a specific index |
composite | true | boolean | undefinedWhether the combobox is a composed with other composite widgets like tabs |
disableLayer | - | boolean | undefinedWhether to disable registering this a dismissable layer |
navigate | - | ((details: NavigateDetails) => void) | null | undefinedFunction to navigate to the selected item |
dir | "ltr" | "ltr" | "rtl" | undefinedThe document's text/writing direction. |
getRootNode | - | (() => ShadowRoot | Node | Document) | undefinedA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. |
onPointerDownOutside | - | ((event: PointerDownOutsideEvent) => void) | undefinedFunction called when the pointer is pressed down outside the component |
onFocusOutside | - | ((event: FocusOutsideEvent) => void) | undefinedFunction called when the focus is moved outside the component |
onInteractOutside | - | ((event: InteractOutsideEvent) => void) | undefinedFunction called when an interaction happens outside the component |
element | - | ((attributes: HTMLAttributes<"div">) => Element) | undefinedRender the element yourself |
RootProvider
| Property | Default | Type |
|---|---|---|
value | - | ComboboxApi<PropTypes, any> |
element | - | ((attributes: HTMLAttributes<"div">) => Element) | undefinedRender the element yourself |
RootContext
| Property | Default | Type |
|---|---|---|
children | - | (combobox: ComboboxApi<PropTypes, any>) => ReactNode |
Label
| Property | Default | Type |
|---|---|---|
element | - | ((attributes: HTMLAttributes<"label">) => Element) | undefinedRender the element yourself |
Control
| Property | Default | Type |
|---|---|---|
element | - | ((attributes: HTMLAttributes<"div">) => Element) | undefinedRender the element yourself |
Input
| Property | Default | Type |
|---|---|---|
element | - | ((attributes: HTMLAttributes<"input">) => Element) | undefinedRender the element yourself |
Trigger
| Property | Default | Type |
|---|---|---|
element | - | ((attributes: HTMLAttributes<"button">) => Element) | undefinedRender the element yourself |
Positioner
| Property | Default | Type |
|---|---|---|
element | - | ((attributes: HTMLAttributes<"div">) => Element) | undefinedRender the element yourself |
Content
| Property | Default | Type |
|---|---|---|
element | - | ((attributes: HTMLAttributes<"ul">) => Element) | undefinedRender the element yourself |
ItemGroup
| Property | Default | Type |
|---|---|---|
element | - | ((attributes: HTMLAttributes<"div">) => Element) | undefinedRender the element yourself |
ItemGroupLabel
| Property | Default | Type |
|---|---|---|
element | - | ((attributes: HTMLAttributes<"div">) => Element) | undefinedRender the element yourself |
Item
| Property | Default | Type |
|---|---|---|
persistFocus | - | boolean | undefinedWhether hovering outside should clear the highlighted state |
item | - | anyThe item to render |
element | - | ((attributes: HTMLAttributes<"li">) => Element) | undefinedRender the element yourself |
ItemText
| Property | Default | Type |
|---|---|---|
element | - | ((attributes: HTMLAttributes<"span">) => Element) | undefinedRender the element yourself |
ItemIndicator
| Property | Default | Type |
|---|---|---|
element | - | ((attributes: HTMLAttributes<"div">) => Element) | undefinedRender the element yourself |