Docs
Combobox
Combobox
Combines a text input with a listbox, allowing users to filter a list of options to items matching a query.
import {
Combobox,
ComboboxContent,
ComboboxInput,
ComboboxItem,
ComboboxTrigger,
} from "@repo/tailwindcss/ui/combobox";
const ALL_OPTIONS = ["Next.js", "Astro", "Qwik", "SolidStart", "Nuxt.js"];
const ComboboxDemo = () => {
return (
<Combobox
options={ALL_OPTIONS}
placeholder="Search framework…"
itemComponent={(props) => (
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
)}
>
<ComboboxTrigger>
<ComboboxInput />
</ComboboxTrigger>
<ComboboxContent />
</Combobox>
);
};
export default ComboboxDemo;
Installation
npx shadcn-solid@latest add combobox
Install the following dependencies:
npm install @kobalte/core
Copy and paste the following code into your project:
import { cn } from "@/libs/cn";
import type {
ComboboxContentProps,
ComboboxInputProps,
ComboboxItemProps,
ComboboxTriggerProps,
} from "@kobalte/core/combobox";
import { Combobox as ComboboxPrimitive } from "@kobalte/core/combobox";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ParentProps, ValidComponent, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
export const Combobox = ComboboxPrimitive;
export const ComboboxDescription = ComboboxPrimitive.Description;
export const ComboboxErrorMessage = ComboboxPrimitive.ErrorMessage;
export const ComboboxItemDescription = ComboboxPrimitive.ItemDescription;
export const ComboboxHiddenSelect = ComboboxPrimitive.HiddenSelect;
type comboboxInputProps<T extends ValidComponent = "input"> = VoidProps<
ComboboxInputProps<T> & {
class?: string;
}
>;
export const ComboboxInput = <T extends ValidComponent = "input">(
props: PolymorphicProps<T, comboboxInputProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxInputProps, ["class"]);
return (
<ComboboxPrimitive.Input
class={cn(
"h-full bg-transparent text-sm placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-50",
local.class,
)}
{...rest}
/>
);
};
type comboboxTriggerProps<T extends ValidComponent = "button"> = ParentProps<
ComboboxTriggerProps<T> & {
class?: string;
}
>;
export const ComboboxTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, comboboxTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxTriggerProps, [
"class",
"children",
]);
return (
<ComboboxPrimitive.Control>
<ComboboxPrimitive.Trigger
class={cn(
"flex h-9 w-full items-center justify-between rounded-md border border-input px-3 shadow-sm",
local.class,
)}
{...rest}
>
{local.children}
<ComboboxPrimitive.Icon class="flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4 opacity-50"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m8 9l4-4l4 4m0 6l-4 4l-4-4"
/>
<title>Arrow</title>
</svg>
</ComboboxPrimitive.Icon>
</ComboboxPrimitive.Trigger>
</ComboboxPrimitive.Control>
);
};
type comboboxContentProps<T extends ValidComponent = "div"> =
ComboboxContentProps<T> & {
class?: string;
};
export const ComboboxContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, comboboxContentProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxContentProps, ["class"]);
return (
<ComboboxPrimitive.Portal>
<ComboboxPrimitive.Content
class={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95 origin-[--kb-combobox-content-transform-origin]",
local.class,
)}
{...rest}
>
<ComboboxPrimitive.Listbox class="p-1" />
</ComboboxPrimitive.Content>
</ComboboxPrimitive.Portal>
);
};
type comboboxItemProps<T extends ValidComponent = "li"> = ParentProps<
ComboboxItemProps<T> & {
class?: string;
}
>;
export const ComboboxItem = <T extends ValidComponent = "li">(
props: PolymorphicProps<T, comboboxItemProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxItemProps, [
"class",
"children",
]);
return (
<ComboboxPrimitive.Item
class={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
local.class,
)}
{...rest}
>
<ComboboxPrimitive.ItemIndicator class="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m5 12l5 5L20 7"
/>
<title>Checked</title>
</svg>
</ComboboxPrimitive.ItemIndicator>
<ComboboxPrimitive.ItemLabel>
{local.children}
</ComboboxPrimitive.ItemLabel>
</ComboboxPrimitive.Item>
);
};
Install the following dependencies:
npm install @kobalte/core
Copy and paste the following code into your project:
import { cn } from "@/libs/cn";
import type {
ComboboxContentProps,
ComboboxInputProps,
ComboboxItemProps,
ComboboxTriggerProps,
} from "@kobalte/core/combobox";
import { Combobox as ComboboxPrimitive } from "@kobalte/core/combobox";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ParentProps, ValidComponent, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
export const Combobox = ComboboxPrimitive;
export const ComboboxDescription = ComboboxPrimitive.Description;
export const ComboboxErrorMessage = ComboboxPrimitive.ErrorMessage;
export const ComboboxItemDescription = ComboboxPrimitive.ItemDescription;
export const ComboboxHiddenSelect = ComboboxPrimitive.HiddenSelect;
type comboboxInputProps<T extends ValidComponent = "input"> = VoidProps<
ComboboxInputProps<T> & {
class?: string;
}
>;
export const ComboboxInput = <T extends ValidComponent = "input">(
props: PolymorphicProps<T, comboboxInputProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxInputProps, ["class"]);
return (
<ComboboxPrimitive.Input
class={cn(
"h-full text-sm placeholder:text-muted-foreground focus:outline-none disabled:(cursor-not-allowed opacity-50) bg-inherit",
local.class,
)}
{...rest}
/>
);
};
type comboboxTriggerProps<T extends ValidComponent = "button"> = ParentProps<
ComboboxTriggerProps<T> & {
class?: string;
}
>;
export const ComboboxTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, comboboxTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxTriggerProps, [
"class",
"children",
]);
return (
<ComboboxPrimitive.Control>
<ComboboxPrimitive.Trigger
class={cn(
"flex h-9 w-full items-center justify-between rounded-md border border-input px-3 shadow-sm bg-inherit",
local.class,
)}
{...rest}
>
{local.children}
<ComboboxPrimitive.Icon class="flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4 opacity-50"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m8 9l4-4l4 4m0 6l-4 4l-4-4"
/>
<title>Arrow</title>
</svg>
</ComboboxPrimitive.Icon>
</ComboboxPrimitive.Trigger>
</ComboboxPrimitive.Control>
);
};
type comboboxContentProps<T extends ValidComponent = "div"> =
ComboboxContentProps<T> & {
class?: string;
};
export const ComboboxContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, comboboxContentProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxContentProps, ["class"]);
return (
<ComboboxPrimitive.Portal>
<ComboboxPrimitive.Content
class={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[expanded]:(animate-in fade-in-0 zoom-in-95) data-[closed]:(animate-out fade-out-0 zoom-out-95) origin-[--kb-combobox-content-transform-origin]",
local.class,
)}
{...rest}
>
<ComboboxPrimitive.Listbox class="p-1" />
</ComboboxPrimitive.Content>
</ComboboxPrimitive.Portal>
);
};
type comboboxItemProps<T extends ValidComponent = "li"> = ParentProps<
ComboboxItemProps<T> & {
class?: string;
}
>;
export const ComboboxItem = <T extends ValidComponent = "li">(
props: PolymorphicProps<T, comboboxItemProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxItemProps, [
"class",
"children",
]);
return (
<ComboboxPrimitive.Item
class={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none data-[disabled]:(pointer-events-none opacity-50) data-[highlighted]:(bg-accent text-accent-foreground)",
local.class,
)}
{...rest}
>
<ComboboxPrimitive.ItemIndicator class="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m5 12l5 5L20 7"
/>
<title>Checked</title>
</svg>
</ComboboxPrimitive.ItemIndicator>
<ComboboxPrimitive.ItemLabel>
{local.children}
</ComboboxPrimitive.ItemLabel>
</ComboboxPrimitive.Item>
);
};
Usage
import {
Combobox,
ComboboxItem,
ComboboxTrigger,
ComboboxContent
} from "@/components/ui/combobox";
import { createFilter } from "@kobalte/core";
import { createSignal } from "solid-js";
const ALL_OPTIONS = ["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"];
const filter = createFilter({ sensitivity: "base" });
const [options, setOptions] = createSignal(ALL_OPTIONS);
const onInputChange = (value: string) => {
setOptions(ALL_OPTIONS.filter(option => filter.contains(option, value)));
};
<Combobox
options={options()}
onInputChange={onInputChange}
itemComponent={props => <ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>}
>
<ComboboxTrigger>
<ComboboxInput />
</ComboboxTrigger>
<ComboboxContent />
</Combobox>