Time picker
Picks a time of day, typed in the field or chosen from a list of times. Holds a time with no date, like a Time field.
Installation
The CLI installs the Radix or Base UI file to match your project, with what it builds on: combobox, input-group.
If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.
When to use
Use it
- People enter a time of day in a form, like an event's start time or a preferred call time.
- People type the time or pick it from a list of times under the field, every 15 minutes by default. The field shows it in the locale's style, "9:30 PM" in en-US and "21:30" in de-DE.
- The value is a time with no date and no time zone, like a Salesforce Time field. The time picker holds it as
"HH:MM", or"HH:MM:SS"with seconds. - Only some times are allowed, like business hours. Pass
minandmax. - It replaces
lightning-inputwithtype="time".
Don't use it
- The value is a date and a time, like a Salesforce Date/Time field. Use Datetime picker.
- The value is a day. Use Date picker.
- People only type the time and don't need a list. Use Input with
type="time", which also gives phones their own time wheel. - The time is a duration, like a call's length in minutes. Use Number input.
- The time is only read. Show it as text.
Usage
import {
TimePicker,
TimePickerContent,
TimePickerInput,
TimePickerList,
} from "@/components/wisty/time-picker"TimePicker
├── TimePickerInput
└── TimePickerContent
└── TimePickerList
└── TimePickerItemTimePicker holds the value, the typed text and the list's state; the other parts only draw. TimePickerList renders a TimePickerItem for each time on its own, so most fields stop at the list. onValueChange gets the time, or null with the reason when the text isn't a time the field accepts:
"use client"
import { useState } from "react"
import { type TimePickerError } from "@/components/wisty/time-picker"export function StartTime() {
const [startTime, setStartTime] = useState<string | null>(null)
const [error, setError] = useState<TimePickerError>()
return (
<TimePicker
name="StartTime"
value={startTime}
onValueChange={(value, details) => {
setStartTime(value)
setError(details.error)
}}
>
<TimePickerInput aria-label="Start time" />
<TimePickerContent>
<TimePickerList />
</TimePickerContent>
</TimePicker>
)
}error is "format" for text it can't read, and "min" or "max" for a time outside the bounds. In a form, name submits the time as "HH:MM", 24-hour. The field reads the locale's style, 24-hour times ("21:30"), AM or PM in a word or a letter ("9:30p", "9pm"), an hour alone ("9") and digits with no separator ("930", "2130"). To accept other text, like "noon", pass your own parse.
Examples
Step and bounds
Business hours only: the list runs from `min` to `max` every 30 minutes with `step`, and a typed time outside them is an error.
Business hours, 8:00 AM to 6:00 PM.
Seconds
A time with seconds, shown, typed and reported as "HH:MM:SS" with `timeStyle="medium"`.
Locale
German on the 24-hour clock, from `locale`: the field and the list show 14:30, and typing reads it.
States
A required field showing the error `onValueChange` reports, a `disabled` one and a `readOnly` one holding a time.
In a dialog
A call time in a modal dialog: the list opens over it, and Escape closes the list first, then the dialog.
API reference
| Part | Built on |
|---|---|
TimePicker | |
TimePickerInput | ComboboxInput |
TimePickerContent | ComboboxContent |
TimePickerList | ComboboxList |
TimePickerItem | ComboboxItem |
TimePicker
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | null | The time of day as 24-hour "HH:MM", or null when the field is empty or its text isn't a time. | |
defaultValue | string | null | null | The time at first, when uncontrolled. |
onValueChange | ( value: string | null, details: TimePickerChangeDetails ) => void | Called with the new time, or null and the reason the text isn't one. | |
min | string | The earliest time people can pick or type, as "HH:MM" or "HH:MM:SS"; the list starts here. | |
max | string | The latest time people can pick or type, as "HH:MM" or "HH:MM:SS". | |
step | number | 15 | Minutes between the times in the list. Typed times aren't rounded to it. |
locale | string | "en-US" | Formats and reads times, and sets the 12- or 24-hour clock. |
timeStyle | TimePickerStyle | "short" | "medium" shows seconds, and takes and reports "HH:MM:SS". |
parse | TimePickerParse | parseTime | Reads typed text into "HH:MM" or "HH:MM:SS", or null when it isn't a time. |
name | string | Submits the value with a form, from a hidden input. | |
required | boolean | false | Required with a form. |
disabled | boolean | false | Disables the field and its list. |
readOnly | boolean | false | Shows the time without the list; it can't be changed. |
children | React.ReactNode | The input and the list. |
TimePickerItem
| Prop | Type | Default | Description |
|---|---|---|---|
value* | string | The time, as "HH:MM" or "HH:MM:SS". |
parseTimefunction
Reads a typed time, in the locale's style or not: "9:30p" and "2130" are "21:30", "9" is "09:00". Returns "HH:MM:SS" with seconds, and null when the text isn't a time.
| Parameter | Type | Default |
|---|---|---|
text* | string | |
__1* | { locale: string; seconds: boolean } |
Returns string | null.
TimePickerErrortype
type TimePickerError = "format" | "min" | "max"TimePickerChangeDetailstype
| Member | Type | Default | Description |
|---|---|---|---|
text* | string | The field's text when the value changed. | |
error | TimePickerError | Why the text isn't a time the field accepts. Unset when it is. |
TimePickerParsetype
type TimePickerParse = (
text: string,
options: { locale: string; seconds: boolean }
) => string | nullTimePickerStyletype
type TimePickerStyle = "short" | "medium"SLDS mapping
The time picker is SLDS's timepicker as lightning-input draws it: the autocomplete combobox with slds-timepicker on the form element. TimePickerInput is slds-combobox__input with the decorative clock icon on the right (slds-input__icon_right, utility:clock). TimePickerContent is the fluid dropdown under the field (slds-dropdown slds-dropdown_fluid), TimePickerList its slds-listbox, and TimePickerItem a plain option (slds-listbox__option_plain) with the check on the selected time (slds-is-selected).
Known gaps:
- SLDS swaps in the phone's own time input on mobile. The time picker keeps its field and list.
- The clock icon is lucide's, where SLDS draws
utility:clock. - The field's error text ("Enter a valid time.", "Value must be … or later.") is English. Times, AM and PM follow
locale.
| Prop | Value | SLDS | Notes |
|---|---|---|---|
(no value) | default (added) | slds-timepicker, slds-combobox__input, slds-input__icon_right (utility:clock), slds-dropdown_fluid, slds-listbox__option_plain | A typed field with the clock on the right, and a list of times under it with a check on the selected one. |
step | (minutes) (added) | lightning-input step | Minutes between the listed times. 15 by default. Typed times aren't rounded to it. |
timeStyle | short, medium (added) | lightning-input time-style | How times show: 9:30 PM (the default) or 9:30:00 PM, in en-US. Medium takes and reports seconds. |
locale | (a locale) (added) | lightning-input's user locale | The 12- or 24-hour clock, the words for AM and PM, and the digits. Defaults to en-US. |
min, max | (a time) (added) | slds-has-error | The list runs from min to max, and a typed time outside them is an error. |
aria-invalid | true (added) | slds-has-error, slds-icon-text-error | Set by the time picker for text it can't read or a time outside the bounds, or by the caller. |
disabled | true (added) | slds-input[disabled] | |
readOnly | true (added) | slds-input[readonly] | The time as text, without the field's frame or the clock. |
Keyboard interactions
| Keys | Action |
|---|---|
| Typing | Narrows the list to the times that start with the text, and opens it when one does. |
| Down Arrow, Up Arrow | Open the list on the picked time, or the first, and move through the times. |
| Enter | Picks the highlighted time. With none highlighted, reads the typed text, or with nothing typed, opens or closes the list. |
| Escape | Closes the list and keeps the field's text. Inside a modal dialog, the first Escape closes only the list, and the next closes the dialog. |
| Tab | Reads the typed text and moves on. Leaving the field does the same. |
A click in the field opens the list on the picked time, and focus stays in the field to type.
Accessibility
- Name the field with a
FieldLabelwhosehtmlForisTimePickerInput'sid, or passaria-labelto it. - For an error you decide, like an empty required field, set
aria-invalidonTimePickerInputand pointaria-describedbyat the message. The time picker setsaria-invaliditself, and the input's validity, for text it can't read and times outsideminandmax. - While the list is open, the rest of the page is hidden from screen readers, the field's label included. The input keeps its name.
- In Cosmos dark the error text is about 4.3:1, below WCAG AA's 4.5:1. SLDS's own stylesheet does the same.
Performance
- The list renders every time in it: 96 at the default 15 minutes. A
stepof a minute makes 1,440; keep it at 5 or more, or narrow it withminandmax.
Rules
- Keep the value a
"HH:MM"or"HH:MM:SS"string, 24-hour and zero-padded. ADatecarries a day and a time zone that a time of day doesn't have. - A
valuethat isn't a time, like"9:00", shows as it is with a"format"error. One outsideminandmaxshows with its error. Neither is moved into range, submitted with a form, or reported throughonValueChange. - Changing
localeortimeStyleshows the value again in the new style. Text typed and not yet read is replaced. - Disabled, the time picker submits nothing with a form, as a disabled input doesn't.
- Never pass
asChildorrender.
Related
- Date pickerPicks a day, typed in the field or chosen from a calendar under it.
- Datetime pickerPicks a date and a time, typed or chosen from a calendar and a list.
- ComboboxFilters a list of options as you type, for one value or several shown as pills.
- InputTakes a single line of text.
- FieldLays out a form control with its label, help text and error.