Skip to contentSkip to navigationSkip to topbar
Paste assistant Assistant
Figma
Star

Time Picker

Version 6.1.1GithubStorybookPeer review pending

A Time Picker is a form element used to select a time.

Component preview theme
const LabelOnlyPicker = (props) => {
const uidTP = useUID();
return (
<>
<Label htmlFor={uidTP}>What time is it locally?</Label>
<TimePicker id={uidTP} {...props} />
</>
);
};
render(<LabelOnlyPicker/>)

Guidelines

Guidelines page anchor

About Time Picker

About Time Picker page anchor

The Time Picker is an input field which accepts a value in HH:mm format.

Currently, the Time Picker is built on top of the HTML time picker(link takes you to an external page), using an input field with type="time", which comes with some inherent limitations. In order to release a functional and stable Time Picker for immediate use, we decided to work within these limitations with the intention of updating this package to a custom Time Picker in a future release. The current API was designed to avoid any breaking changes with future versions of the Time Picker.

It is used with the Label and Help Text components.

Because browsers' implementations of the native time picker vary, this component may not be fully accessible in all cases. Chrome/Edge, Safari and Firefox all support the Time Picker, but the user experience differs browser-to-browser. Some of those differences are outlined below:

Chrome/Edge
  • Default value is "--:-- --"
  • Clock pop-up is triggered by clicking on the clock icon - clicking on the input field places focus on the value
  • Scrollable time selector pop-up
Chrome/Edge implementation of time picker
Safari
  • Default value is 12:30 PM
  • No clock icon or time selector pop-up, only usable by keyboard (number or arrow keys)
  • Clicking on the input field places focus on the hour value
Safari implementation of time picker
Firefox
  • Default value is "--:-- --"
  • No clock icon or time selector pop-up, only usable by keyboard (number or arrow keys)
  • Clicking on the input field places focus on the hour value
Firefox implementation of time picker

While cross-browser functionality, style and accessibility differences are present, they will not affect the usability of the Time Picker. The API is stable and full usage of the component is encouraged. There are certain use cases not included in the input type="time" functionality, such as a time range picker. You can find examples of how to recreate those use cases below. If there's a Time Picker use case not covered by the current implementation, feel free to open a Discussion(link takes you to an external page) so that we can help you find a solution.

  • Include a visible label on all Time Pickers.
  • Each label must use the htmlFor prop that matches the id of the associated input.
  • Use one of 3 ways to label a Time Picker:
    • Visible label with Label (preferred)
    • Visible label that's associated to the Time Picker with aria-labelledby
    • Label directly using aria-label
  • Provide clear identification of required fields in the label or at the start of a form. If you use the required field indicator, include the form key at the start of the form.
    • Use the required prop to programatically indicate they are required to browsers.
  • Include inline error text with the error icon on any field that errors to make it visually clear that the field changed.
  • If the Time Picker has associated Help Text or error text, include the aria-describedby prop on the Time Picker. This should match the id of the help or error text.

The Time Picker component should include the base TimePicker, along with supporting elements to compose an input field that gives users the context they need to successfully fill it out.

  • Label — A label should be included for every Time Picker and provide a short title for its associated input.
  • Required field indicator — In a form where there are fewer or as many required fields as optional, use a required indicator to show users which fields are required to be filled out.
  • Help text — Text that's placed below the field to help users prevent an error and describe what makes the Time Picker input successful.
(information)

Make sure to connect the Label to the TimePicker. This is done with the htmlFor prop on the label, and the id prop on the Time Picker. Those two need to match.

If the Time Picker has any associated Help Text, it should also use the aria-describedby prop that equals the id of the Help Text. This ensures screen readers know the Help Text ties directly to the Time Picker.

Component preview theme
const RequiredTimePicker = (props) => {
const uidTP = useUID();
const uidHT = useUID();
return (
<>
<Label htmlFor={uidTP} required>
What time is your workday over?
</Label>
<TimePicker required id={uidTP} aria-describedby={uidHT} {...props} />
<HelpText id={uidHT}>Select a time.</HelpText>
</>
);
};
render(<RequiredTimePicker/>)

The Time Picker doesn't currently support the selection of time ranges within a single input field, however a time range picker can be easily implemented using two side-by-side Time Pickers.

Component preview theme
const TimeRangePicker = (props) => {
const startUidTP = useUID();
const endUidTP = useUID();
return (
<Stack orientation="horizontal" spacing="space80">
<Box>
<Label htmlFor={startUidTP}>Start time</Label>
<TimePicker id={startUidTP} {...props} />
</Box>
<Box>
<Label htmlFor={endUidTP}>End time</Label>
<TimePicker id={endUidTP} {...props} />
</Box>
</Stack>
);
};
render(<TimeRangePicker/>)

Paste pickers don't currently support date time range selections in a single component, however the same functionality can be implemented using a combobox along with the date range and time range picker solutions. If you'd like to provide your users with pre-determined options before displaying the pickers, use the following example as a jumping off point.

Component preview theme
const DateTimeRangePicker = (props) => {
const [selectedItem, setSelectedItem] = React.useState('')
const uidStartDP = useUID();
return selectedItem === "Custom" ? (
<>
<Box margin="space60">
<Label htmlFor="uidStartDP">Date range</Label>
</Box>
<Stack orientation="horizontal" spacing="space80">
<Box marginLeft="space60">
<DatePicker id="uidStartDP" aria-describedby="start-date" />
<HelpText id="start-date">Start date</HelpText>
</Box>
<Box>
<TimePicker aria-describedby="start-time" />
<HelpText id="start-time">Start time</HelpText>
</Box>
</Stack>
<Stack orientation="horizontal" spacing="space80">
<Box marginLeft="space60" marginTop="space60">
<DatePicker aria-describedby="end-date" />
<HelpText id="end-date">End date</HelpText>
</Box>
<Box marginTop="space60">
<TimePicker aria-describedby="end-time" />
<HelpText id="end-time">End time</HelpText>
</Box>
</Stack>
</>
) : (
<Box margin="space60">
<Combobox
insertAfter={<CalendarIcon color="colorTextIcon" decorative />}
items={["Last 7 days", "Last 30 days", "Last 6 months", "Custom"]}
labelText="Date range"
onSelectedItemChange={(changes) => {
setSelectedItem(changes.selectedItem)
}}
/>
</Box>
)
}
render(<DateTimeRangePicker />)

In addition to the Time Picker, this package exports a formatReturnTime() function that can be used to format the time value that's being returned from the Time Picker.

Note: the format of the time being displayed by the Time Picker cannot be changed. The display value is formatted according to the locale of the user's browser(link takes you to an external page). The parsed value format, however, which is returned in HH:mm format (using a 24-hour time format), can be changed.

To change the format of the return time value, we recommend using the formatReturnTime() function on the Time Picker's onChange() or onBlur() handler.

The function accepts two parameters: (1) the time value that should be formatted (e.g. '14:52'), and (2) the desired time format (e.g. 'mm:hh aa'). It uses a library called date-fns(link takes you to an external page) to return the given time in the desired format. Time format must follow the tokens outlined by date-fns(link takes you to an external page).

Component preview theme
const OnChangePicker = (props) => {
const [value, setValue] = React.useState('');
const [timeFormat, setTimeFormat] = React.useState('HH:mm');
const uidTP = useUID();
const uidHT = useUID();
const timeFormatOptions = ['hh:mm aa', 'hh:mm a', 'HH:mm'];
const handleChange = (val, format) => {
setValue(formatReturnTime(val, format));
return value;
};
return (
<>
<Box marginBottom="space60">
<Combobox
items={timeFormatOptions}
labelText="Return time format:"
onInputValueChange={({inputValue}) => {
if (inputValue !== undefined) setTimeFormat(inputValue);
}}
/>
</Box>
<Label htmlFor={uidTP}>What time were you born?</Label>
<TimePicker
id={uidTP}
aria-describedby={uidHT}
onChange={(evt) => {
handleChange(evt.target.value, timeFormat);
}}
{...props}
/>
<HelpText id={uidHT}>Select a time above.</HelpText>
<Box marginTop="space60">
{' '}
<b>Return value:</b> {value}{' '}
</Box>
</>
);
};
render(<OnChangePicker/>)

Time Picker with inline error

Time Picker with inline error page anchor

Change a Time Picker to its error state and add an inline error if the value entered doesn't pass validation requirements.

Use Help Text to show an inline error below the field to inform a user of any errors in their value. Change the Help Text to variant=“error” and add error copy before the original help text copy.

Component preview theme
const HasErrorPicker = (props) => {
const uidTP = useUID();
const uidHT = useUID();
return (
<>
<Label htmlFor={uidTP}>When does your workday start?</Label>
<TimePicker id={uidTP} aria-describedby={uidHT} hasError {...props} />
<HelpText id={uidHT} variant="error">
Select a time.
</HelpText>
</>
);
};
render(<HasErrorPicker/>)

Use a disabled Time Picker to show users that they can't interact with it.

If you want to show information that can't be edited, use a read-only field or consider another way of showing static information.

Component preview theme
const DisabledPicker = (props) => {
const uidTP = useUID();
const uidHT = useUID();
return (
<>
<Label htmlFor={uidTP} disabled>
Set a curfew for your child
</Label>
<TimePicker id={uidTP} aria-describedby={uidHT} disabled {...props} />
<HelpText id={uidHT}>Choose a time.</HelpText>
</>
);
};
render(<DisabledPicker/>)

Use a read-only Time Picker when a its value can't be changed, but users should be able to read or apply focus on it.

Component preview theme
const ReadOnlyPicker = (props) => {
const uidTP = useUID();
const uidHT = useUID();
return (
<>
<Label htmlFor={uidTP}>What&apos;s your favorite time of day?</Label>
<TimePicker id={uidTP} aria-describedby={uidHT} readOnly value="13:00" {...props} />
<HelpText id={uidHT}>Choose a time.</HelpText>
</>
);
};
render(<ReadOnlyPicker/>)

A Time Picker must have at least a label and an input.

Component preview theme
const LabelOnlyPicker = (props) => {
const uidTP = useUID();
return (
<>
<Label htmlFor={uidTP}>What time is it locally?</Label>
<TimePicker id={uidTP} {...props} />
</>
);
};
render(<LabelOnlyPicker/>)
(information)

Quick note about Time Picker DOM methods

There are three methods that come with the HTML picker: stepUp(), stepDown() and select(). Usage of these methods is possible, but we do not encourage it, as they may not be supported in future versions of the Time Picker. Use at your own risk!

Positioning Time Pickers

Positioning Time Pickers page anchor

When using multiple Time Pickers, stack them vertically with $space-80 spacing between each field. To stack them, you can use either a Box or a Stack (as seen in the example below).

Component preview theme
const StackOfPickers = (props) => {
const uidTPOne = useUID();
const uidHTOne = useUID();
const uidTPTwo = useUID();
const uidHTTwo = useUID();
return (
<Stack orientation="vertical" spacing="space80">
<Box>
<Label htmlFor={uidTPOne}>What time is it now?</Label>
<TimePicker id={uidTPOne} aria-describedby={uidHTOne} {...props} />
<HelpText id={uidHTOne}>Select a time above.</HelpText>
</Box>
<Box>
<Label htmlFor={uidTPTwo}>What time was it 5 minutes ago?</Label>
<TimePicker id={uidTPTwo} aria-describedby={uidHTTwo} {...props} />
<HelpText id={uidHTTwo}>Select a time above.</HelpText>
</Box>
</Stack>
);
};
render(<StackOfPickers/>)

Avoid placing multiple pickers on the same horizontal row to help make it easier to scan a page vertically. Use a Grid if you need to place them horizontally.

Labels should clearly describe the time value being requested. They should be concise and descriptive, not instructional. To do this:

  • Use Help Text to provide instruction if needed. For example, don't use "Enter the date you wish to receive your bill below" as label text. Instead, use "Billing date" as a label and "Your account will be automatically billed on the above date." as Help Text.
  • Avoid articles ("a", "the") and punctuation. For example, use "SIM registration code" rather than "The SIM registration code".

To support internationalization, avoid starting a sentence with the label and using the field to finish it since sentence structures vary across languages. For example, use "Call log expiration time" or "How long should logs be stored?". Don't use "Remove logs after:".

Required field indicator

Required field indicator page anchor

Use required indicators to show users which fields they must fill out.

Component preview theme
const RequiredTimePicker = (props) => {
const uidTP = useUID();
const uidHT = useUID();
return (
<>
<Label htmlFor={uidTP} required>
What time is your workday over?
</Label>
<TimePicker required id={uidTP} aria-describedby={uidHT} {...props} />
<HelpText id={uidHT}>Select a time.</HelpText>
</>
);
};
render(<RequiredTimePicker/>)

Validate Time Picker fields on form submission.

Validating a field input when the user leaves the current field (on blur) can be helpful to check for syntax errors. However, this can be frustrating to users who tab through controls to navigate a page, and to screen reader users, who might not be able to detect that an error occurred on blur.

Don't prevent form submission by disabling the submit button. A disabled button cannot be detected by assistive technologies. Use error messages to explain what information is missing or incorrect.

Usage of the min and max properties on Time Picker is discouraged as browser support is varied and limited.

Use inline error text to explain how to fix an error. For additional guidance on how to compose error messages, refer to the error state pattern.

Use Help Text to help users avoid errors.

SituationRecommended phrasing
User didn't pick a timeSelect a time.
The end time is before the start dateThe end time occurs before the start time.
The start date is after the end dateThe start time occurs after the end time.
(information)

Time Picker and placeholder

Because Time Picker has a default display value of --:-- -- (and defaults to 12:30 PMon Safari), any value that gets passed into placeholder will be overwritten (and effectively ignored).