A native time field with optional quick-pick presets and controlled or uncontrolled state.
Schedule a reminder
Pick a time that works for your day.
import { TimePicker } from "@/components/ballmac/time-picker"
const presets = [
{ label: "Morning", value: "09:00" },
{ label: "Midday", value: "12:00" },
{ label: "Afternoon", value: "15:00" },
]
export default function TimePickerDemo() {
return (
<div className="w-full max-w-sm rounded-xl border bg-card p-5 shadow-sm">
<p className="mb-1 text-sm font-semibold">Schedule a reminder</p>
<p className="mb-4 text-xs text-muted-foreground">
Pick a time that works for your day.
</p>
<TimePicker
label="Reminder time"
defaultValue="09:00"
presets={presets}
/>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/time-pickerInstall the dependencies.
$ pnpm add lucide-reactAdd the Ballmac items it builds on.
$ pnpm dlx shadcn@latest add @ballmac/i18nCopy the source into your project.
components/ballmac/time-picker.tsx// Ballmac UI: Time Picker. https://ui.ballmac.com/components/time-picker "use client" import * as React from "react" import { Clock } from "lucide-react" import { cn } from "@/lib/utils" import { useMessages } from "@/lib/ballmac/i18n" type TimePreset = { label: string; value: string } type TimePickerProps = Omit< React.ComponentProps<"input">, "type" | "value" | "defaultValue" | "onChange" > & { /** Controlled time in HH:mm format. */ value?: string /** Initial time when uncontrolled. */ defaultValue?: string /** Called with the new HH:mm time. */ onValueChange?: (value: string) => void /** Accessible name of the time field. */ label?: string /** Optional shortcuts to common times. */ presets?: TimePreset[] } function TimePicker({ value, defaultValue = "", onValueChange, label, presets = [], className, disabled, ...props }: TimePickerProps) { const msg = useMessages() label ??= msg("time-picker.label", "Time") const [internal, setInternal] = React.useState(defaultValue) const current = value ?? internal function commit(next: string) { if (value === undefined) setInternal(next) onValueChange?.(next) } return ( <div data-slot="time-picker" className={cn("w-full min-w-0", className)}> <div className="flex h-9 items-center gap-2 rounded-md border border-input bg-background px-3 shadow-xs transition-[border-color,box-shadow] duration-150 motion-reduce:transition-none focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50"> <Clock aria-hidden="true" className="size-4 shrink-0 text-muted-foreground" /> <input data-slot="time-picker-input" type="time" aria-label={label} disabled={disabled} value={current} onChange={(event) => commit(event.currentTarget.value)} className="h-full min-w-0 flex-1 bg-transparent text-sm text-foreground outline-none disabled:opacity-50" {...props} /> </div> {presets.length > 0 && ( <div data-slot="time-picker-presets" className="mt-2 flex flex-wrap gap-1.5" > {presets.map((preset) => ( <button key={preset.value} type="button" disabled={disabled} onClick={() => commit(preset.value)} className={cn( "rounded-md border border-border px-2.5 py-1 text-xs outline-none hover:bg-accent focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:opacity-50", current === preset.value && "border-ring bg-accent font-medium", )} > {preset.label} </button> ))} </div> )} </div> ) } export { TimePicker, type TimePickerProps, type TimePreset }Update the import paths to match your project setup.
Usage
import { TimePicker } from "@/components/ballmac/time-picker"The full example is in the Code tab above.
Examples
States and variants
Office hours end
import { TimePicker } from "@/components/ballmac/time-picker"
export default function TimePickerStates() {
return (
<div className="w-full max-w-sm">
<p className="mb-2 text-sm font-medium">Office hours end</p>
<TimePicker
label="Office hours end"
defaultValue="17:30"
min="09:00"
max="20:00"
/>
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
valueControlled time in HH:mm format. | string | — |
defaultValueInitial time when uncontrolled. | string | "" |
onValueChangeCalled with the new HH:mm time. | (value: string) => void | — |
labelAccessible name of the time field. | string | — |
presetsOptional shortcuts to common times. | TimePreset[] | [] |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Arrow keys | Edits the native time field |
| Tab / Enter | Activates a time preset |
Use with AI
A native time field with optional quick-pick presets and controlled or uncontrolled state. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Time Picker (@ballmac/time-picker) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Schedule an appointment or reminder
- Offer common meeting times as shortcuts
Not for
- Use date-range-picker for a period across days
Registry JSON: https://ui.ballmac.com/r/time-picker.json
Credits
Free to use in personal and commercial projects.
- npm
- lucide-react
- Registry
- @ballmac/i18nshadcn/utils
Pairs well with
Input
A text input in three heights that match Button, plus InputGroup and InputGroupAddon for leading or trailing icons, units and domains.
Calendar
A date grid for one day, several days or a range, with month and year selects, week numbers, disabled rules and range-end styling, on React DayPicker.
Color Picker
A native color chooser paired with an editable hex field and a live swatch.
Combobox
A searchable single-choice select with groups, descriptions, keywords, clearable value, invalid state and hidden-input form submission.