<!-- https://svgrid.com/docs/help/ui-components/sv-time-picker/ - SvGrid documentation as markdown. Index of every page: https://svgrid.com/llms.txt -->

# 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](https://svgrid.com/docs/help/ui-components/date-time/) ·
[SvDateTimePicker](https://svgrid.com/docs/help/ui-components/sv-date-time-picker/) · [SvCalendar](https://svgrid.com/docs/help/ui-components/sv-calendar/)

## Installation

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

<div data-docs-add="add time-picker"></div>

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:

<div data-docs-install="@svgrid/grid"></div>

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

```svelte
<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>
```

```ts
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.

> Live demo: Time picker - https://svgrid.com/demos/251-timepicker/

```svelte
<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](https://svgrid.com/docs/help/ui-components/sv-field/) 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](https://svgrid.com/docs/help/ui-components/sv-date-time-picker/)'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

- `TimeValue = Date | string | number | null`
- `TimeFormat = '12-hour' | '24-hour'`
- `TimeSelection = 'hour' | 'minute'`

## 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:

```svelte
<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`:

```svelte
<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:

```svelte
<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:

```svelte
<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

- Renders a `role="group"` with a labelled dial; the core owns the ARIA and
  keyboard model.
- Keyboard: arrow keys nudge the active field (hour or minute); the hour /
  minute segment buttons switch focus between dials.
- Pointer drag uses pointer capture, so dragging the hand keeps tracking outside
  the dial and works with touch.
- `disabled` and `readonly` block interaction; `dir="rtl"` mirrors the layout.

## More examples

### Time picker - headless

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

> Live demo: Time picker - headless - https://svgrid.com/demos/277-headless-timepicker/

## 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.

```svelte
<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.

```svelte
<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

- [Date & time overview](https://svgrid.com/docs/help/ui-components/date-time/) - the whole family at a glance.
- [SvDateTimePicker](https://svgrid.com/docs/help/ui-components/sv-date-time-picker/) - composes this picker behind a TIME tab.
- [SvCalendar](https://svgrid.com/docs/help/ui-components/sv-calendar/) - the date half of the pair.

---

SvGrid is the Svelte 5 data grid (`npm install @svgrid/grid`, MIT). This page: https://svgrid.com/docs/help/ui-components/sv-time-picker/ . All docs: https://svgrid.com/llms.txt
