Primitives
Select
A control that opens a list of options and takes one value.
New invoices use this currency.
import { Field, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'ferry-ui'
export default function SelectHero() {
return (
<Field label="Currency" hint="New invoices use this currency." className="w-full max-w-xs">
{(control) => (
<Select defaultValue="eur">
<SelectTrigger {...control} className="w-full">
<SelectValue placeholder="Select a currency" />
</SelectTrigger>
<SelectContent>
<SelectItem value="usd">US dollar</SelectItem>
<SelectItem value="eur">Euro</SelectItem>
<SelectItem value="gbp">Pound sterling</SelectItem>
<SelectItem value="jpy">Japanese yen</SelectItem>
<SelectItem value="chf">Swiss franc</SelectItem>
</SelectContent>
</Select>
)}
</Field>
)
}aria-label.value of a SelectItem cannot be an empty string. Use a value such as "none".| Choice | Control |
|---|---|
| One value among 4 to 15 options | Select |
| One value among 2 to 5 options | Radio Group |
| One value among 2 to 6 options with a description or an icon | Radio Card Group |
| One value in a long list, or a list with a search field | Command in a Popover |
Import the parts and put them together.
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from 'ferry-ui'
<Select>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel />
<SelectItem />
</SelectGroup>
<SelectSeparator />
<SelectItem />
</SelectContent>
</Select>| Part | Role |
|---|---|
Select | Holds the value and the open state. |
SelectTrigger | The button that opens the list. |
SelectValue | Shows the text of the selected option, or the placeholder. |
SelectContent | The overlay that holds the options. |
SelectItem | One option. |
SelectGroup | A set of options below a SelectLabel. |
SelectLabel | The heading of a group. It is not an option. |
SelectSeparator | A line between two groups. |
SelectScrollUpButton, SelectScrollDownButton | The scroll buttons of a long list. SelectContent adds them for you. |
In a Field, pass a function as the child. The function gets the props of the control. Spread them on SelectTrigger.
To link a Label yourself, pass the same value to htmlFor and to the id of SelectTrigger.
The trigger is as wide as its content. In a form, add className="w-full".
import { Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'ferry-ui'
export default function SelectLabelFor() {
return (
<div className="flex w-full max-w-xs flex-col gap-2">
<Label htmlFor="member-role">Role</Label>
<Select defaultValue="member">
<SelectTrigger id="member-role" className="w-full">
<SelectValue placeholder="Select a role" />
</SelectTrigger>
<SelectContent>
<SelectItem value="viewer">Viewer</SelectItem>
<SelectItem value="member">Member</SelectItem>
<SelectItem value="billing">Billing manager</SelectItem>
<SelectItem value="admin">Admin</SelectItem>
</SelectContent>
</Select>
</div>
)
}The size prop of SelectTrigger sets the height. The three heights are the same as for Input and Button. There is no lg size.
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'ferry-ui'
const SIZES = ['tiny', 'sm', 'md'] as const
export default function SelectSizes() {
return (
<>
{SIZES.map((size) => (
<Select key={size} defaultValue="30d">
<SelectTrigger size={size} aria-label={`Date range, size ${size}`}>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="24h">Last 24 hours</SelectItem>
<SelectItem value="7d">Last 7 days</SelectItem>
<SelectItem value="30d">Last 30 days</SelectItem>
<SelectItem value="90d">Last 90 days</SelectItem>
</SelectContent>
</Select>
))}
</>
)
}| Size | Height | Use |
|---|---|---|
tiny | 26px | Toolbars. |
sm | 30px | Filters and dense forms. |
md | 34px | Form fields. It is the default. |
SelectGroup and SelectLabel give a heading to a set of options. SelectSeparator adds a line between two groups.
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from 'ferry-ui'
export default function SelectGroups() {
return (
<Select defaultValue="europe-paris">
<SelectTrigger className="w-64" aria-label="Time zone">
<SelectValue placeholder="Select a time zone" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Americas</SelectLabel>
<SelectItem value="america-new-york">New York (UTC−05:00)</SelectItem>
<SelectItem value="america-chicago">Chicago (UTC−06:00)</SelectItem>
<SelectItem value="america-los-angeles">Los Angeles (UTC−08:00)</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Europe</SelectLabel>
<SelectItem value="europe-london">London (UTC+00:00)</SelectItem>
<SelectItem value="europe-paris">Paris (UTC+01:00)</SelectItem>
<SelectItem value="europe-berlin">Berlin (UTC+01:00)</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Asia Pacific</SelectLabel>
<SelectItem value="asia-tokyo">Tokyo (UTC+09:00)</SelectItem>
<SelectItem value="australia-sydney">Sydney (UTC+10:00)</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
)
}SelectValue shows its placeholder when the select has no value.
A select holds its value by default. To control the value, pass value and onValueChange. An empty value shows the placeholder again.
import * as React from 'react'
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'ferry-ui'
export default function SelectControlled() {
const [language, setLanguage] = React.useState('')
return (
<>
<Select value={language} onValueChange={setLanguage}>
<SelectTrigger className="w-48" aria-label="Language">
<SelectValue placeholder="Select a language" />
</SelectTrigger>
<SelectContent>
<SelectItem value="en">English</SelectItem>
<SelectItem value="fr">French</SelectItem>
<SelectItem value="de">German</SelectItem>
<SelectItem value="es">Spanish</SelectItem>
<SelectItem value="ja">Japanese</SelectItem>
</SelectContent>
</Select>
{/* An empty value shows the placeholder again. */}
<Button variant="ghost" disabled={language === ''} onClick={() => setLanguage('')}>
Clear
</Button>
</>
)
}By default, the list covers the trigger. With position="popper", the list opens below the trigger. It is then as wide as the trigger or wider. It also follows side, align and sideOffset.
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'ferry-ui'
export default function SelectPopper() {
return (
<Select defaultValue="fr">
<SelectTrigger className="w-48" aria-label="Language">
<SelectValue />
</SelectTrigger>
<SelectContent position="popper">
<SelectItem value="en">English</SelectItem>
<SelectItem value="fr">French</SelectItem>
<SelectItem value="de">German</SelectItem>
<SelectItem value="es">Spanish</SelectItem>
<SelectItem value="ja">Japanese</SelectItem>
</SelectContent>
</Select>
)
}An option can start with an icon. SelectItem sets the size and the color of the icon. The trigger shows the icon of the selected option.
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'ferry-ui'
import { Banknote, CreditCard, Landmark, Wallet } from 'lucide-react'
export default function SelectIcons() {
return (
<Select defaultValue="card">
<SelectTrigger className="w-56" aria-label="Payment method">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="card">
<CreditCard />
Credit card
</SelectItem>
<SelectItem value="transfer">
<Landmark />
Bank transfer
</SelectItem>
<SelectItem value="wallet">
<Wallet />
Digital wallet
</SelectItem>
<SelectItem value="cash">
<Banknote />
Cash
</SelectItem>
</SelectContent>
</Select>
)
}disabled on Select disables the trigger. disabled on a SelectItem disables one option.
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'ferry-ui'
export default function SelectDisabled() {
return (
<>
<Select disabled defaultValue="eur">
<SelectTrigger className="w-40" aria-label="Currency">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="usd">US dollar</SelectItem>
<SelectItem value="eur">Euro</SelectItem>
<SelectItem value="gbp">Pound sterling</SelectItem>
</SelectContent>
</Select>
<Select defaultValue="member">
<SelectTrigger className="w-40" aria-label="Role">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="viewer">Viewer</SelectItem>
<SelectItem value="member">Member</SelectItem>
<SelectItem value="admin">Admin</SelectItem>
<SelectItem value="owner" disabled>
Owner, by transfer only
</SelectItem>
</SelectContent>
</Select>
</>
)
}aria-invalid on SelectTrigger shows the error border. Field sets it when you pass error.
import * as React from 'react'
import { Button, Field, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast } from 'ferry-ui'
export default function SelectInvalid() {
const [role, setRole] = React.useState('')
const [error, setError] = React.useState<string>()
function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
setError(role === '' ? 'Select a role for this member.' : undefined)
if (role !== '') toast.success('Invitation sent')
}
function change(value: string) {
setRole(value)
setError(undefined)
}
return (
<form noValidate onSubmit={submit} className="flex w-full max-w-xs flex-col items-start gap-4">
<Field label="Role" error={error} className="w-full">
{(control) => (
<Select value={role} onValueChange={change}>
<SelectTrigger {...control} className="w-full">
<SelectValue placeholder="Select a role" />
</SelectTrigger>
<SelectContent>
<SelectItem value="viewer">Viewer</SelectItem>
<SelectItem value="member">Member</SelectItem>
<SelectItem value="billing">Billing manager</SelectItem>
<SelectItem value="admin">Admin</SelectItem>
</SelectContent>
</Select>
)}
</Field>
<Button type="submit">Send invitation</Button>
</form>
)
}Each part also accepts the props of its Radix UI primitive and the attributes of its element.
| Prop | Type | Default |
|---|---|---|
children | ReactNode | - |
| See the element or the Radix UI primitive this part renders. | ||
open | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
defaultOpen | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
onOpenChange | ((open: boolean) => void) | - |
| See the element or the Radix UI primitive this part renders. | ||
dir | "ltr" | "rtl" | - |
| See the element or the Radix UI primitive this part renders. | ||
name | string | - |
| See the element or the Radix UI primitive this part renders. | ||
autoComplete | string | - |
| See the element or the Radix UI primitive this part renders. | ||
disabled | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
required | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
form | string | - |
| See the element or the Radix UI primitive this part renders. | ||
value | string | - |
| See the element or the Radix UI primitive this part renders. | ||
defaultValue | string | - |
| See the element or the Radix UI primitive this part renders. | ||
onValueChange | ((value: string) => void) | - |
| See the element or the Radix UI primitive this part renders. | ||
| Prop | Type | Default |
|---|---|---|
size | "default" | "tiny" | "sm" | "md" | md |
Height, on the | ||
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
| Prop | Type | Default |
|---|---|---|
placeholder | ReactNode | - |
| See the element or the Radix UI primitive this part renders. | ||
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
| Prop | Type | Default |
|---|---|---|
forceMount | true | - |
Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. | ||
onCloseAutoFocus | ((event: Event) => void) | - |
Event handler called when auto-focusing on close. Can be prevented. | ||
onEscapeKeyDown | ((event: KeyboardEvent) => void) | - |
Event handler called when the escape key is down. Can be prevented. | ||
onPointerDownOutside | ((event: PointerDownOutsideEvent) => void) | - |
Event handler called when the a | ||
position | "item-aligned" | "popper" | item-aligned |
| See the element or the Radix UI primitive this part renders. | ||
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
side | "top" | "right" | "bottom" | "left" | - |
| See the element or the Radix UI primitive this part renders. | ||
sideOffset | number | - |
| See the element or the Radix UI primitive this part renders. | ||
align | "center" | "start" | "end" | center |
| See the element or the Radix UI primitive this part renders. | ||
alignOffset | number | - |
| See the element or the Radix UI primitive this part renders. | ||
arrowPadding | number | - |
| See the element or the Radix UI primitive this part renders. | ||
avoidCollisions | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
collisionBoundary | Boundary | Boundary[] | - |
| See the element or the Radix UI primitive this part renders. | ||
collisionPadding | number | Partial<Record<"top" | "right" | "bottom" | "left", number>> | - |
| See the element or the Radix UI primitive this part renders. | ||
sticky | "partial" | "always" | - |
| See the element or the Radix UI primitive this part renders. | ||
hideWhenDetached | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
updatePositionStrategy | "always" | "optimized" | - |
| See the element or the Radix UI primitive this part renders. | ||
| Prop | Type | Default |
|---|---|---|
valueRequired | string | - |
| See the element or the Radix UI primitive this part renders. | ||
disabled | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
textValue | string | - |
| See the element or the Radix UI primitive this part renders. | ||
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
| Prop | Type | Default |
|---|---|---|
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
| Prop | Type | Default |
|---|---|---|
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
| Prop | Type | Default |
|---|---|---|
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
| Prop | Type | Default |
|---|---|---|
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
| Prop | Type | Default |
|---|---|---|
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||