Components · Date, time and scheduling
TimezoneSelect
Searchable IANA time zone picker labelled by UTC offset and city, with detected and pinned zones.
Detected and pinned zones
Reports and reminders use this zone.
import { useState } from 'react';
import { Field, FieldDescription, FieldLabel } from '@rookcom/monarch-react';
import { TimezoneSelect } from '@rookcom/monarch-react/scheduling';
const NOW = Date.UTC(2026, 0, 15, 12);
function WorkspaceZone() {
const [zone, setZone] = useState<string | null>('Asia/Kolkata');
return (
<Field style={{ width: '100%', maxWidth: 320 }}>
<FieldLabel>Workspace time zone</FieldLabel>
<TimezoneSelect
value={zone}
onValueChange={setZone}
now={NOW}
detectedZone="Asia/Kolkata"
pinned={['Europe/London', 'America/New_York']}
placeholder="Search city, country or offset"
/>
<FieldDescription>Reports and reminders use this zone.</FieldDescription>
</Field>
);
}
<WorkspaceZone />Usage examples
Curated list, small
import { TimezoneSelect } from '@rookcom/monarch-react/scheduling';
const NOW = Date.UTC(2026, 0, 15, 12);
const COMMON = [
'UTC',
'America/Los_Angeles',
'America/Chicago',
'America/New_York',
'Europe/London',
'Europe/Berlin',
'Asia/Dubai',
'Asia/Kolkata',
'Asia/Singapore',
'Asia/Tokyo',
'Australia/Sydney',
];
<div style={{ width: '100%', maxWidth: 260 }}>
<TimezoneSelect
aria-label="Sending time zone"
size="sm"
zones={COMMON}
now={NOW}
defaultValue="Europe/Berlin"
detectedZone={null}
/>
</div>Purpose
Chooses an IANA time zone from every zone the runtime knows (about 420), labelled "(UTC+05:30) Kolkata" and sorted by offset, with search by city, country, offset or abbreviation. Replaces native selects and hard-coded lists of 15 zones. Import from @rookcom/monarch-react/scheduling.
Use when
- A person, workspace, schedule or booking page needs a time zone.
- A short curated list is enough: pass
zones.
Avoid when
- Showing a time in a zone: format it with Intl instead.
- Picking a UTC offset rather than a place: offsets change with daylight saving time.
Design principles
- Zones are places, not offsets: the value is the IANA id, and the label shows the offset for
now(DST-aware). - The likely answer is first: the device’s zone under "Detected", then the product’s "Pinned" and "Recent" zones.
- Same render on server and client: detection runs after mount, and
nowcan be passed. - Legacy ids that browsers still report (Asia/Calcutta) select their current name (Asia/Kolkata).
API reference
import { TimezoneSelect, canonicalTimezone, formatUtcOffset, getTimezoneInfo, listTimezones } from '@rookcom/monarch-react/scheduling';| Export | Kind | Rendering | Root class |
|---|---|---|---|
TimezoneSelect | component | client | .mn-timezone-select |
canonicalTimezone | utility | client | – |
formatUtcOffset | utility | client | – |
getTimezoneInfo | utility | client | – |
listTimezones | utility | client | – |
Built on Combobox and accepts its input props (size, placeholder, invalid, disabled, limit, …). Forwards its ref to the input.
Helpers: getTimezoneInfo(zone, { now, locale }), listTimezones(), formatUtcOffset(minutes), canonicalTimezone(zone).
Generated from the TypeScript declarations of packages/react/src/components/scheduling/timezone-select.
Properties
Without Intl.supportedValuesOf (older browsers and Node) the list falls back to about 80 common zones; a value outside the list is still shown.
TimezoneSelect
Searchable IANA time zone picker, labelled by UTC offset and city and sorted by offset, with "Detected", "Pinned" and "Recent" sections. Search matches city, zone id, offset (`+5:30`, `utc-8`), long name and common country names. Offsets are DST-aware for `now`. Built on Combobox.
| Prop | Type | Description |
|---|---|---|
children | ReactNode | |
className | string | |
clearable | boolean | Allow clearing the selection with the clear button / Backspace on empty input. |
clearOption | ComboboxClearOption | The list starts with an option that clears the selection, such as "Unassigned" or "No country". |
closeOnSelect | boolean | Close the list after a pick. Default: true, or false with `multiple` or `inline`. |
defaultOpen | boolean | |
defaultValue | string | null | |
detectedZone | string | null | The zone shown under "Detected". Default: the device's zone, read after mount so server and client render the same. `null` hides the section. |
disabled | boolean | |
emptyMessage | ReactNode | Shown when no option matches. |
formatLabel | ((zone: TimezoneInfo) => string) | Option label. Default `(UTC+05:30) Kolkata`. |
id | string | |
inputValue | string | Text typed in the input (controlled). Use for server-side filtering. |
invalid | boolean | |
limit | number | Render at most this many matching options, with a message to keep typing. Default: all. |
listLabel | string | Accessible name of the listbox. Default: the Field label, `aria-label`, or "Options". |
loading | boolean | Shown while options load. |
locale | string | Locale for long zone names ("India Standard Time"). Default `en-US`. |
messages | (Partial<TimezoneSelectMessages> & Partial<ComboboxMessages>) | Interface text, for translation. |
minSearchLength | number | Shortest query `onSearch` runs for; shorter queries show `emptyMessage`. Default 0. |
now | number | Date | The instant offsets are computed for. Default: the time of first render. Pass it to render the same on server and client. |
onInputValueChange | ((text: string) => void) | |
onOpenChange | ((open: boolean) => void) | |
onValueChange | ((zone: string | null) => void) | |
open | boolean | Whether the list is open (controlled). Ignored with `inline`. |
pinned | string[] | Zones listed first under "Pinned", such as the workspace's offices. |
recent | string[] | Zones listed under "Recent", such as the person's last choices. |
renderOption | ((option: ComboboxOption, state: ComboboxOptionState) => ReactNode) | Custom option content. Default: media, label, description, hint and a check mark. |
searchDebounce | number | Milliseconds to wait after typing before `onSearch`. Default 250. |
size | "sm" | "md" | "lg" | |
value | string | null | Selected IANA zone id (controlled). Legacy ids select their current name (`Asia/Calcutta` → `Asia/Kolkata`); a zone the list does not contain is still shown. |
zones | string[] | Limit the list to these zones. Default: every zone the runtime knows, or a static list. |
Variants
| Prop | Values | Description |
|---|---|---|
size | 'sm''md''lg' | Height, as Combobox. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Sections | role="group" | "Detected", "Pinned", "Recent" and "All time zones" while nothing is typed. |
| Searching | – | A flat list of matches in offset order. |
| Empty | role="status" | "No time zones match". |
| Selected / invalid / disabled | – | As Combobox. |
Interaction behaviour
- Typing filters by every word: city, IANA id, long name ("India Standard Time"), offset (
+5:30,utc-8,+0530) or country and abbreviation for common zones ("india", "us pacific", "bst"). - The full list is built only when the field is first focused or opened, so pages with many fields stay fast.
- A zone listed under "Detected" or "Pinned" also appears in its place in "All time zones".
Keyboard support
| Keys | Action |
|---|---|
| ArrowDown / ArrowUp | Opens the list, then moves. |
| PageDown / PageUp, Home / End | Moves ten zones, or to the first or last. |
| Enter | Selects the active zone. |
| Escape | Closes and restores the selected label. |
Accessibility
- Role
- combobox, controlling a listbox
- Accessible name
- A FieldLabel or
aria-label.
- Each option reads its offset, city, IANA id and long name.
- Sections are labelled groups.
- Inherits Combobox’s APG keyboard and
aria-activedescendantsupport.
Responsive behaviour
- The list matches the field width (at least 12rem); long descriptions wrap.
Theme support
- Combobox tokens; the input uses tabular numbers so offsets line up.
Do and don't
Do
- Store the IANA id the picker returns, not the offset.
- Pin the zones the workspace already uses (offices, teammates).
- Pass
nowwhen the page is server rendered and must match exactly.
Don't
- Do not hard-code a short list of zones unless the product truly only supports those.
- Do not show offsets without a place; "UTC+01:00" is London in summer and Lagos all year.
Related components
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | react |
| Monarch components | Combobox |
Uses only Intl; no time zone data is bundled beyond the fallback id list and search aliases.
Testing coverage
- Offsets formatted; DST-aware offsets, city and long name for an instant; unknown zones fall back.
- Static list when
Intl.supportedValuesOfis missing; legacy ids canonicalised. - Detected, Pinned, Recent and All sections in offset order; device detection after mount.
- Search by country, offset and "us pacific"; selects; empty message.
- A value outside the list, a legacy value and a custom label.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery/timezone-select; Cross-browser axe: /components/timezone-select |
Tests: packages/react/src/components/scheduling/timezone-select