SvTimePicker

An analog clock-dial time picker - 12- or 24-hour, minute snapping, and an hour-to-minute auto-switch. Drag the hand or click a number.

Related: Date & time overview · SvDateTimePicker · SvCalendar

Installation

Add it with the CLI - this drops a ready-to-edit SvTimePicker starter into your app:

Prefer to see it first? npx @svgrid/ui try time-picker opens it in a throwaway sandbox - no project needed.

Or install the package and import it directly. SvTimePicker ships free in @svgrid/grid (no extra date library) and is the same component SvGrid mounts to edit a time cell - so it is both a standalone control and the grid's built-in time editor:

The examples on this page import from @svgrid/grid:

<script lang="ts">
  import { SvTimePicker } from '@svgrid/grid'

  // The bound value behind each example below.
  let slot = $state<Date | null>(null)
  let existing = $state<Date | null>(null)
</script>
import { SvTimePicker } from '@svgrid/grid'

Example

SvTimePicker is a styled renderer over the headless createTimePicker core: the core owns the value math, dial geometry, keyboard and ARIA, while the component keeps the DOM-bound pointer capture and hit-testing. All visuals come from the grid's --sg-* tokens, so every theme applies for free, in light and dark. It is the editor SvGrid mounts for a time cell, and it works standalone anywhere.

Open the live example: Time picker (Date & Time)

<script lang="ts">
  import { SvTimePicker } from '@svgrid/grid'
  let time = $state(new Date())
</script>

<SvTimePicker
  value={time}
  format="12-hour"
  minuteInterval={5}
  footer
  onChange={(d) => (time = d)}
/>

Props

Pass any of label / hint / error (plus required / invalid / id) to wrap the dial in SvField chrome (a label + hint/error line) when using it as a standalone form control; omit them and it renders bare, as it does inside SvDateTimePicker's popover.

Prop Type Default Description
value TimeValue null A Date, "HH:MM[:SS]" string, or epoch ms.
onChange (value: Date) => void - Fires with a Date (today's date carrying the picked time).
format 12-hour | 24-hour 24-hour Clock face and readout format.
minuteInterval number 1 Snap minutes to this step.
autoSwitchToMinutes boolean true After picking an hour, jump the dial to minutes.
footer boolean false Show the Now footer button.
disabled boolean false Blocks interaction and dims the control.
readonly boolean false Shows the value but blocks changes.
name string - Emits a hidden input carrying HH:MM.
selection hour | minute hour Which dial opens first.
dir ltr | rtl | auto auto Text direction; rtl mirrors the layout.
messages Partial<TimeMessages> - Override the built-in strings (group label + AM/PM/Now).

Helper types

Examples

Quarter-hour scheduling

Constrain to coarse steps with minuteInterval so bookings land on clean boundaries; the dial snaps the hand to the nearest allowed minute:

<SvTimePicker value={slot} minuteInterval={15} onChange={(d) => (slot = d)} />

String value in, Date out

Feed a plain "HH:MM" string (handy for form state) and read a Date back from onChange:

<script lang="ts">
  let raw = $state('09:30')
</script>

<SvTimePicker value={raw} onChange={(d) => (raw = d.toTimeString().slice(0, 5))} />

12-hour with a Now shortcut

Turn on footer for a one-click Now, and format="12-hour" adds the AM/PM toggle beside the readout:

<SvTimePicker format="12-hour" footer onChange={(d) => (time = d)} />

Adjusting an existing time (minutes first)

Open the minute dial directly with selection="minute", and keep it there by turning off the hour-to-minute auto-switch, so a small tweak to an existing value does not jump dials:

<SvTimePicker
  value={existing}
  selection="minute"
  autoSwitchToMinutes={false}
  onChange={(d) => (existing = d)}
/>

Tip: onChange always emits a Date carrying today's date with the picked time, even when you feed a "HH:MM" string in - convert back with d.toTimeString().slice(0, 5) if your state is a string.

Accessibility

More examples

Time picker - headless

Styled analog SvTimePicker and a custom digital readout, sharing one value.

Open the live example: Time picker - headless (Headless Editors)

In a form

The shared field props behave the same on every editor: label names it, hint explains it, and error plus invalid mark it - which is why a validated form does not need per-component handling.

<script lang="ts">
  import { SvTimePicker } from '@svgrid/grid'

  let time = $state<Date | null>(null)
</script>

<SvTimePicker
  value={time}
  label="Label"
  hint="A short hint"
  required
/>

<SvTimePicker
  value={time}
  label="Label"
  error="Something is wrong"
  invalid
/>

Disabled and read-only

Disabled takes the control out of the tab order; read-only keeps it focusable and copyable. Reach for read-only when the value still matters to the reader.

<script lang="ts">
  import { SvTimePicker } from '@svgrid/grid'

  let time = $state<Date | null>(null)
</script>

<SvTimePicker value={time} disabled />

<SvTimePicker value={time} readonly />

See also

Live examples

  • Time picker - SvTimePicker: an analog clock-dial picker with 12/24-hour, minute snapping and hour to minute auto-switch. Drag the hand or click a number. The SvGrid time cell editor, usable standalone in any SvGrid app.
  • Time picker - headless - Styled analog SvTimePicker and a custom digital readout, sharing one value.