v1.0 · Production-grade Select for shadcn/ui

The Select shadcn/ui is missing.

Single, multiple, and tags modes. Built-in search, virtualized lists, and full keyboard & screen-reader support — production-grade behavior in one copy-paste component.

Try it

terminal
npx shadcn@latest add https://gjs-select.gokhanyildiz.dev/r/gjs-select.json

Get started

Installation

Add the component with the shadcn CLI — it drops select.tsx straight into your components/ui. No package to install, no styles to import. You own the code and edit it like any other file.

1. Run the shadcn CLI

terminal
npx shadcn@latest add https://gjs-select.gokhanyildiz.dev/r/gjs-select.json

2. Use it

example.tsx
import { Select } from "@/components/ui/select"

const options = [
  { label: "Apple", value: "apple" },
  { label: "Banana", value: "banana" },
  { label: "Cherry", value: "cherry" },
]

export function Example() {
  return (
    <Select
      options={options}
      showSearch
      allowClear
      placeholder="Pick a fruit"
    />
  )
}

Showcase

Every mode, out of the box

Single, multiple, and tags modes; search, clear, optgroups, sizes, and built-in states — all driven by simple props.

Modes

Single by default, or mode="multiple | tags"

Search, clear & groups

showSearch · allowClear · optgroups

Sizes

size="small | middle | large"

States

disabled · loading · status=error

Patterns

Real-world patterns

Server and client rendering, debounced server-side search, and responsive tag overflow — the cases you actually ship.

Server & client components

The same Select rendered by a Server Component and a Client Component, side by side.

Server ComponentRendered on the server · Node v24.15.0

No 'use client' here. Options are prepared on the server and the Select is shipped as a client island.

Client ComponentServer HTML (pre-hydration)

'use client' with live state — selected: bun.

Debounced async search

showSearch · filterOption={false} · onSearch → fetch · loading — server-side filtering over a live API route.

Responsive tag overflow

maxTagCount="responsive" collapses tags that no longer fit into a +N pill as the control resizes.

← Drag the dashed box wider or narrower →

Playground

Try it live

Tweak the props and copy the generated code straight into your project.

Mode

Size

Options

preview.tsx
<Select
  options={options}
  showSearch
  allowClear
/>

Reference

Props API

A curated set of the most-used props. The component also forwards refs and standard combobox ARIA attributes.

PropTypeDefaultDescription
optionsSelectItem<V>[][]Options or optgroups to render. { label, value, disabled? }.
value / defaultValueV | V[] | nullControlled / uncontrolled selected value(s).
onChange(value, option) => voidFires when the selection changes.
mode"multiple" | "tags"Multi-select, or tags mode that creates new entries.
showSearchbooleanfalse*Filterable search input inside the control (* on by default in multi).
allowClearbooleanfalseShow a clear button to reset the value on hover.
size"small" | "middle" | "large""middle"Control height and typography.
status"error" | "warning"Validation status styling for the control.
variant"outlined" | "filled" | "borderless""outlined"Visual style of the control.
loadingbooleanfalseShow a spinner and loading state.
maxCountnumberCap selections; remaining options disable at the limit.
tokenSeparatorsstring[]Split typed/pasted input into multiple tags.
virtualbooleanfalseWindowed rendering for very long option lists.
filterSort(a, b, info) => numberCustom ordering for the filtered options.
labelInValuebooleanfalseEmit { label, value } objects in onChange.
fieldNames{ label, value, options }Map custom keys on your option data.