Skip to content

TimePicker

Takes a time of day from hour, minute and second columns. Each column is a scrolling listbox.

tsx
import { TimePicker } from 'neba';

<TimePicker label="Starts at" placeholder="Pick a time" minuteStep={15} clearable />;

Props

PropTypeDefaultDescription
variantshared'solid' | 'outline' | 'text''outline'Weight of the surface: filled, hairline, or none
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Height and type scale. A row of the clock is the same height
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'Semantic colour role. Arbitrary colour values are not accepted
densityshared'default' | 'compact''default'Padding only: never the height, never the type scale
elevationshared0 | 1 | 2 | 30Drop shadow depth. 0 means no shadow at all
valueDate | nullThe chosen time. A Date, so it carries a day as well: see referenceDate
defaultValueDate | nullThe initial value, for an uncontrolled picker
onValueChange(value: Date | null) => voidCalled when the chosen time changes
referenceDateDatetodayThe day a chosen time is written onto while there is no value yet
minTimeDate | nullThe earliest time of day that may be chosen: only the clock is read. Compared against the span a row stands for, so 09:30 keeps the hour 9 and greys out the minutes before it
maxTimeDate | nullThe other end of the same span
hour12booleanA 12-hour dial with an AM/PM column. Defaults to whatever the locale does
showSecondsbooleanfalseAdds the seconds column
hourStepnumber1How far apart the rows of the hour column are
minuteStepnumber1The same, for minutes
secondStepnumber1The same, for seconds
shouldDisableTime(value: Date, unit: 'hour' | 'minute' | 'second' | 'meridiem') => booleanBlocks individual rows. Called once per row per column with the instant that row would produce and the column it is in
showNowButtonbooleantrueOffers the shortcut to the current time in the footer
openbooleanWhether the popup is open. Use with onOpenChange for a controlled one
defaultOpenbooleanfalseWhether it starts open
onOpenChange(open: boolean) => voidCalled when the popup opens or closes
localestringBCP 47 tag deciding the month and weekday names, the order of the header's two buttons, and how the trigger writes the value. Defaults to the browser's
formatIntl.DateTimeFormatOptionsHow the trigger writes the value. Passed straight to Intl
placeholderReactNodeShown in the trigger while nothing is chosen
clearablebooleanfalseOffers the × that empties the control
closeOnSelectbooleanfalseCloses the popup as soon as a column is touched. False by default, unlike DatePicker: a time is two answers, and closing after the first would make choosing 9:30 a matter of opening the popup twice
labelsPartial<PickerLabels>The strings a screen reader hears. One object rather than twenty props, because they are a set: they default to the locale’s wording, and the date names are not among them, those come from Intl
namestringIdentifies the field when a form is submitted, as HH:MM (HH:MM:SS with seconds shown)
fullWidthbooleanfalseStretches to the width of the container
labelReactNodeThe label, wired to the control by Base UI's Field
descriptionReactNodeHelper text
errorReactNodeError message. Its presence turns the control invalid and re-points the colour family at danger
invalidboolean!!errorForces the invalid state without a message, for when a form library owns validity
readOnlybooleanfalseShown but not changeable. Keeps its colour and edge, drains the saturation
disabledbooleanfalseUnavailable. Drops the colour family for neutral grey

Native <div> attributes pass through to the root. Only color, defaultValue and children are excluded, since the table above spells them differently.

value is a Date | null. referenceDate is the day a chosen time is written onto while the value is still empty; it defaults to today and is held still for as long as the picker is mounted, so a popup left open across midnight does not move the value onto a new day.

closeOnSelect defaults to false: an hour and a minute both have to be given, so the popup does not close on the first, and the footer carries a Done button.

Examples

minuteStep · secondStep · hour12

minuteStep and secondStep are the intervals each column lists. hour12 follows the locale by default, and a 12-hour column runs 12, 1, 2 … 11 rather than 0…11.

minTime · maxTime · shouldDisableTime

minTime and maxTime are compared against the span a row stands for, not a single instant. With a minimum of 09:30, the hour 9 covers 09:00–09:59 and overlaps what is allowed, so it stays available while 00 through 25 grey out in the minute column. That is what keeps 09:30 reachable.

shouldDisableTime is handed the instant a row would produce and the column it belongs to, so a rule may be as coarse as "no lunch hour" or as fine as one minute.

showNowButton and clearable

showNowButton adds a button that jumps to the current time; clearable adds one that empties the value.

Accessibility

  • Each column is a role="listbox" of role="option" rows. The column names come from the locale, and labels writes them out instead.
  • The chosen row carries aria-selected; a blocked one carries aria-disabled rather than the disabled attribute, so it stays reachable and announces why it cannot be taken.
  • The chosen row in each column is scrolled into view when the popup opens.
  • A live region beside the columns reads out the whole time whenever it changes.

Released under the MIT License