Usage
import { TimePicker } from "@voila.dev/ui/time-picker";A field that opens a list of times rather than a clock. Picking from a list is
faster than typing when the answer is one of a known set — appointment slots,
opening hours, a shift start. When the time is arbitrary to the minute, the
native field is the better tool: NativeDatePicker.Time lets someone type
14:37 without scrolling past it.
The value is a "HH:mm" string, not a Date. A time with no date attached does
not need one, and storing it as text avoids a timezone question that has no
answer — 09:00 at a clinic is 09:00 wherever the reader happens to be.
Recipes
Opening hours, quarter-hour slots
min and max bound the list inclusively and step sets the spacing, so a
clinic offering quarter-hour appointments between nine and half five is three
props rather than a generated array. The list is built from these, which means
an out-of-hours time cannot be picked — though, as ever, it can still arrive
from stored data.
locale and formatOptions control how the times are displayed without
touching what is stored: a French reader sees 14:30 and an American reader
2:30 PM, and both produce the string "14:30".
Set name and the field writes a hidden input carrying that same HH:mm, so a
plain form post gets the value with no client code.
API
| Prop | Type | Default | |
|---|---|---|---|
defaultOpen | boolean | — | Whether the list starts open. Uncontrolled — for the controlled form use open. |
defaultValue | string | — | Initial "HH:mm" value when uncontrolled. |
formatOptions | DateTimeFormatOptions | — | Intl.DateTimeFormat options for the labels. Defaults to a short localized
time ({ hour: "numeric", minute: "2-digit" }, e.g. "2:30 PM" / "14:30"). |
loading | boolean | — | Show a leading spinner and mark the button aria-busy, disabling
interaction while an async action is in flight (e.g. a form submit). |
locale | string | — | BCP-47 locale (e.g. "fr-FR"), applied to the trigger and option labels. |
max | string | "23:59" | Last selectable time, "HH:mm" inclusive. |
min | string | "00:00" | First selectable time, "HH:mm" inclusive. |
name | string | — | Name for the hidden form input; the value is serialized as HH:mm. |
onOpenChange | ((open: boolean) => void) | — | Called when the list opens or closes. |
onValueChange | ((time: string | null) => void) | — | Called with the picked "HH:mm", or null when cleared. Never a Date. |
open | boolean | — | Controlled open state. Pair it with onOpenChange. |
placeholder | string | "Pick a time" | Shown on the trigger while nothing is selected. |
shape | "default" | "pill" | — | pill forces a fully rounded button; default follows the theme's
--radius, so it changes with the brand. |
size | "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | — | Height and horizontal padding. The icon-* set is square, for a button
whose entire content is one icon — give those an aria-label. |
step | number | 30 | Minutes between two options. Defaults to 30. |
value | string | null | — | Controlled "HH:mm" value; pass null for a controlled empty selection. |
variant | "brand" | "default" | "destructive" | "ghost" | "highlight" | "link" | "outline" | "primary" | "secondary" | — | Weight of the action. default is the one thing the page exists for, at
most one per view; secondary an equal-weight alternative; outline
for toolbars and anything that repeats in a row; ghost inside dense
surfaces; destructive for what cannot be undone; link for an action
that reads as navigation. |
className | string | ((state: ButtonState) => string | undefined) | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
focusableWhenDisabled | boolean | — | Whether the button should be focusable when disabled. |
nativeButton | boolean | — | Whether the component renders a native <button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>). |
render | ComponentRenderFn<HTMLProps, ButtonState> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
style | CSSProperties | ((state: ButtonState) => CSSProperties | undefined) | — | Style applied to the element, or a function that returns a style object based on the component's state. |
Plus the DOM props of the element it renders. Source: time-picker/components/time-picker.tsx.