Select

Select provides a dropdown interface for choosing from predefined options. It supports single and multi-selection modes, disabled states, validation, and customizable styling.

Basics

Simple single-value select with helper copy and live selection feedback.

Loading demo…

import { Select } from '@plocks/ui' import { sports } from '../data' export function Demo() { return ( <Select label="Favorite sport" description="Choose your favorite sport" placeholder="Choose a sport" options={sports} /> ) }

Variants

Select accepts the same variant prop as <Input> — default, filled, outline, unstyled — and shares the underlying input styles, so the trigger reads consistently with text inputs in the same form.

Loading demo…

import { useState } from 'react' import { Block, Select } from '@plocks/ui' import { sports } from '../data' const variants = [ { variant: 'default', label: 'Default' }, { variant: 'filled', label: 'Filled' }, { variant: 'outline', label: 'Outline' }, { variant: 'unstyled', label: 'Unstyled' }, ] as const export function Demo() { const [value, setValue] = useState<string | null>(null) return ( <Block flex> {variants.map(({ variant, label }) => ( <Select key={variant} variant={variant} label={label} placeholder="Pick one…" options={sports} value={value} onChange={(v) => setValue(v as string | null)} /> ))} </Block> ) }

Custom rendering

Render each option with additional detail and selection styling using renderOption.

Loading demo…

import { useState } from 'react' import { Block, Icon, Select, Text, useTheme } from '@plocks/ui' import { detailedSports, type DetailedSport } from '../data' export function Demo() { const theme = useTheme() const [value, setValue] = useState<string | null>(detailedSports[0].value) const accent = theme.colorScheme === 'dark' ? theme.colors.primary[5] : theme.colors.primary[6] return ( <Block w={400}> <Select label="Choose a sport" placeholder="Pick a sport" options={detailedSports} value={value} onChange={(selected) => setValue(selected as string)} renderOption={(option, active, selected) => { const { emoji, name, description } = option as DetailedSport return ( <Block direction="row" align="center" gap={12} style={{ padding: 12, borderLeftWidth: 3, borderLeftColor: active || selected ? accent : 'transparent', backgroundColor: selected ? theme.colors.primary[0] : undefined, }} > <Text size="3xl">{emoji}</Text> <Block direction="column" style={{ flex: 1 }} gap={0}> <Text fw={selected ? '900' : '600'}>{name}</Text> <Text size="sm" c="secondary"> {description} </Text> </Block> {selected ? <Icon name="check" size={16} color={accent} /> : null} </Block> ) }} /> </Block> ) }

Disabled states

Disable individual options or the full control to reflect availability.

Loading demo…

import { useState } from 'react' import { Block, Select } from '@plocks/ui' import { sports } from '../data' // One option is taken out of play to show the per-option disabled state next to // the whole-field one. const options = sports.map((option) => option.value === 'basketball' ? { ...option, label: 'Basketball (disabled)', disabled: true } : option, ) export function Demo() { const [value, setValue] = useState<string | null>(sports[0].value) return ( <Block flex direction="row"> <Select label="Disabled option example" options={options} value={value} onChange={(val) => setValue(val as string)} /> <Select label="Entire select disabled" options={options} value={value} disabled /> </Block> ) }

Persistent menu

Keep the dropdown open after each choice for quick comparisons.

Loading demo…

import { useState } from 'react' import { Select } from '@plocks/ui' import { sports } from '../data' export function Demo() { const [value, setValue] = useState<string | null>(null) return ( <Select label="Persistent menu" description="Menu doesn't close on option press" options={sports} value={value} onChange={(val) => setValue(val as string)} closeOnSelect={false} /> ) }
Scan to open this page on your phoneplocks.dev/ui/Select