Select with motion: the list springs out of its trigger with its options rising in, one highlight glides between them, and a new value rises into the trigger.
$ pnpm dlx shadcn@latest add https://dashral.com/r/motion-select.jsonInstallation
$ pnpm dlx shadcn@latest add https://dashral.com/r/motion-select.jsonAdds components/dashral/motion/select.tsx, the Select it builds on (in components/dashral/), motion and the motion files every motion component shares. With the registry in your components.json:
$ pnpm dlx shadcn@latest add @dashral/motion-selectUsage
The same as Select, imported from components/dashral/motion/: swap the import and it moves.
import { Select } from "@/components/dashral/motion/select"
<Select aria-label="Status" value={status} onValueChange={setStatus} options={["Open", "In progress", "Done"]} />Examples
A status filter
Pick a status: tickets that no longer match fold away, the rest glide up, and the count turns on its wheels.
Compared with Default
The list fades and zooms in from 95%; the value in the trigger changes at once.
Motion
The list springs out with its options rising in; one highlight glides between them; the picked tick draws; the chevron turns; a new value rises into the trigger.
How the two editions differ in general, and which to use where: Motion.
API reference
Every prop of Select, and every attribute of the element it renders. The Motion edition adds no props of its own.
Select
Also takes every prop of Radix Select(opens in a new tab).
| Prop | Type | Default |
|---|---|---|
value / defaultValue | string | — |
onValueChange | (value: string) => voidCalled with the new value. | — |
options | (string | { value; label?; description?; icon?; disabled? } | { label?; options })[]The choices, or groups of them (a line between groups). Required. | — |
placeholder | ReactNodeShown while nothing is picked. | — |
prefix | ReactNodeInside the trigger before the value, e.g. an icon. | — |
size | "default" | "sm"The trigger's height. | "default" |
side | "top" | "bottom"Which side the list opens on. | "bottom" |
label | ReactNodeShown above, linked to the control. | — |
description | ReactNodeA hint under the control. | — |
error | boolean | ReactNodetrue shows the error style; a message also shows under the control. | — |
labelAction | ReactNodeAt the end of the label's row, e.g. a “Forgot password?” link or the current value. | — |
withAsterisk | booleanMarks the label with an asterisk, e.g. alongside required. | false |
classNames | { root, label, description, error, trigger, content, item }Classes for each part. | — |
className | stringThe trigger's classes. | — |
Motion
- The list springs out of its trigger (--duration-standard, a hint of overshoot), its options rising 4px as they fade in, one after another; it leaves quickly.
- The highlight is one pill gliding from option to option after the pointer or the arrow keys.
- The picked option's tick draws itself along its stroke.
- The chevron turns over as the list opens and back as it closes; a new value rises into the trigger as the old one leaves upward.
- Closed, the options stay with Radix for the native select a form submits and for typing on the trigger. With reduced motion it only fades.