Skip to content

shadcn.io is an independent project built on shadcn/ui

Free

Collapsible Groups Combobox for Svelte

A department combobox whose Engineering group starts open and whose Marketing group starts closed.

UI library: Ark UI

Each heading is a button with a chevron. Engineering starts open, with Software, Hardware, and QA. Marketing starts closed, with Content, Social Media, and SEO. A click on the heading toggles its rows.

Use it for a long grouped list when people usually need one section. Short groups can stay open under a Combobox label. In Svelte, Ark UI does not collapse the groups. The headings do.

Preview

Preview width

Installation

MCP · Pro
Make your AI a shadcn expert
Your agent installs components, builds pages and restyles your app from one prompt, in Claude Code, Cursor or VS Code.
Part of Pro

Anatomy

Combobox anatomy: control, input, trigger, content, item, item indicator

Usage

<Combobox collection={collection} onInputValueChange={handleInputValueChange} onValueChange={handleValueChange} positioning={{ getAnchorElement: () => triggerRef }} selectionBehavior="clear" value={value}><ComboboxTrigger class={cn(
          buttonVariants({ variant: "outline" }),
          "w-[250px] justify-between"
        )} bind:ref={triggerRef}>{#if selected}{selected.label}{:else}{"Select department..."}{/if}</ComboboxTrigger><ComboboxContent><ComboboxInput class="w-full" placeholder="Search..." showTrigger={false} /><ComboboxEmpty>No department found.</ComboboxEmpty><ComboboxList>{#each groupNames as group (group)}{@const isCollapsed = Boolean(collapsed[group])}{@const items = collection.items.filter(
              (item) => item.group === group
            )}{#if items.length === 0 && !(isCollapsed && inputValue === "")}{:else}<ComboboxGroup><ComboboxGroupLabel><button aria-expanded={!isCollapsed} class="flex w-full items-center gap-1" data-group={group} onclick={handleToggle} type="button">{#if isCollapsed}<ChevronRightIcon aria-hidden="true" class="size-3" />{:else}<ChevronDownIcon aria-hidden="true" class="size-3" />{/if}{group}</button></ComboboxGroupLabel>{#each items as item (item.value)}<ComboboxItem item={item}>{item.label}</ComboboxItem>{/each}</ComboboxGroup>{/if}{/each}</ComboboxList></ComboboxContent></Combobox>

FAQ

How is the collapsed state stored?

In a record of group names, which starts with Marketing collapsed. The heading button flips the entry for its group, read from a data-group attribute.

What is in each heading?

A button inside the group label with a ChevronRightIcon when closed or a ChevronDownIcon when open, then the group name. It sets aria-expanded to match.

What happens to the items of a closed group?

They aren't rendered, so the arrow keys skip them. They're also taken out of the collection with set, then the current search is applied again with filter.

Which departments are listed?

Engineering: Software, Hardware and QA. Marketing: Content, Social Media and SEO. The button shows Select department... until one is picked.

Can the headings be toggled from the keyboard?

The arrow keys move through items only, so reach the heading buttons with Tab, or click them. For a list people mostly drive by keyboard, keep the groups open.

API Reference

Combobox

Sets collection={collection}, onInputValueChange={handleInputValueChange}, onValueChange={handleValueChange}, positioning, getAnchorElement and cn. The rest is on the Combobox page.