Async option loading
A Combobox driven by useAsyncOptions: debounced server-side search with loading / empty states. shouldFilter is off so the server owns filtering.
Preview
Source
apps/docs/app/recipes/combobox-async/recipe.tsx'use client';
import { Combobox, useAsyncOptions, type ComboboxOption } from '@hey-mike/tungsten';
const PEOPLE = ['Ada Lovelace', 'Alan Turing', 'Grace Hopper', 'Katherine Johnson', 'Edsger Dijkstra'];
// Fake server: filter + latency.
function searchPeople(query: string): Promise<ComboboxOption[]> {
return new Promise((resolve) => {
setTimeout(() => {
const q = query.toLowerCase();
resolve(
PEOPLE.filter((p) => p.toLowerCase().includes(q)).map((p) => ({ id: p, label: p })),
);
}, 400);
});
}
export default function ComboboxAsyncRecipe() {
const { options, isLoading, error, onInputChange } = useAsyncOptions(searchPeople);
return (
<div className="w-72">
<Combobox
label="Search people"
options={options}
isLoading={isLoading}
error={error}
onInputChange={onInputChange}
shouldFilter={false}
placeholder="Type a name…"
emptyMessage="No people found"
/>
</div>
);
}
'use client';
import { Combobox, useAsyncOptions, type ComboboxOption } from '@hey-mike/tungsten';
const PEOPLE = ['Ada Lovelace', 'Alan Turing', 'Grace Hopper', 'Katherine Johnson', 'Edsger Dijkstra'];
// Fake server: filter + latency.
function searchPeople(query: string): Promise<ComboboxOption[]> {
return new Promise((resolve) => {
setTimeout(() => {
const q = query.toLowerCase();
resolve(
PEOPLE.filter((p) => p.toLowerCase().includes(q)).map((p) => ({ id: p, label: p })),
);
}, 400);
});
}
export default function ComboboxAsyncRecipe() {
const { options, isLoading, error, onInputChange } = useAsyncOptions(searchPeople);
return (
<div className="w-72">
<Combobox
label="Search people"
options={options}
isLoading={isLoading}
error={error}
onInputChange={onInputChange}
shouldFilter={false}
placeholder="Type a name…"
emptyMessage="No people found"
/>
</div>
);
}