---
title: Time Picker
description: Time selection input
visibility: guest
draft: false
---

# Time Picker

The `<x-wirekit::time-picker>` component is a styled wrapper around the native `<input type="time">`. It uses the browser's built-in time selection UI for zero dependencies and automatic localization.

## Basic Usage

:::preview{title="Basic Time Picker"}
<x-wirekit::time-picker label="Appointment" name="appointment" value="14:30" />
:::

## 12-Hour Format

:::preview{title="12-Hour Display"}
<x-wirekit::time-picker label="Meeting time" name="meeting" format="12h" value="09:00" />
:::

The `format` prop controls the visual display. Regardless of format, the submitted value is always in 24-hour `HH:MM` format (the native `<input type="time">` standard).

> **Note:** The 12-hour vs 24-hour display is ultimately controlled by the browser and OS locale. The `format` prop provides a hint but browsers may override it.

## With Step (Minutes Interval)

:::preview{title="15-Minute Intervals"}
<x-wirekit::time-picker label="Slot" name="slot" :step="900" hint="Available in 15-minute intervals" />
:::

The `step` attribute is in seconds. Common values: `60` (1 min), `300` (5 min), `900` (15 min), `1800` (30 min), `3600` (1 hour).

## Size Variants

:::preview{title="Three Sizes"}
<x-wirekit::time-picker name="t-sm" label="Small" size="sm" value="08:00" />

<x-wirekit::time-picker name="t-md" label="Medium" size="md" value="12:00" />

<x-wirekit::time-picker name="t-lg" label="Large" size="lg" value="18:00" />
:::

## Error State

:::preview{title="Time picker with an error message"}
<x-wirekit::time-picker label="Start time" name="start-error-demo" error="Please select a valid time." />
:::

## With Hint

:::preview{title="Hint Text"}
<x-wirekit::time-picker label="Reminder" name="reminder" hint="We will notify you at this time daily." />
:::

## Width

Like all WireKit form components, the time picker fills its container (`w-full`). Time fields are typically narrow — constrain the width via the parent element:

```blade
<div class="max-w-[10rem]">
    <x-wirekit::time-picker label="Start time" name="start_time" />
</div>
```

See [Input — Width](/components/input#width) for more layout examples (grid columns, mixed widths).

## Why Native?

The native `<input type="time">` provides:

- **Zero dependencies** — no JavaScript time picker library to ship
- **Automatic localization** — the browser renders AM/PM or 24-hour based on OS locale
- **Full accessibility** — arrow keys, spinners, and keyboard input work out of the box
- **Mobile keyboards** — native time wheels on iOS, spinners on Android
- **Consistent value format** — always `HH:MM` regardless of display

## Optimistic UI

Pass the name of the Livewire method this component should call and the change appears immediately, then confirms or undoes itself when the server answers:

```blade
<x-wirekit::time-picker
    name="start"
    :value="$start"
    optimistic="setStart"
/>
```

Load `wirekit-optimistic.js` alongside whichever bundle you already use — it is a separate file so applications that do not use it pay nothing for it:

```blade
@wirekitScripts
<script src="{{ asset('vendor/wirekit/wirekit-optimistic.js') }}"></script>
```

### Try it

The demo below runs the real path: the change shows immediately, the outline says it is
provisional, and the server's answer either confirms it silently or takes it back.

:::preview{title="Optimistic time — accepted, refused, and a slow answer"}
<livewire:demos.optimistic-host>
<x-wirekit::stack gap="lg" style="max-width: 26rem;">
    <x-wirekit::time-picker name="opt-accept" label="Accepted — the confirmation is silent" value="09:30" optimistic="demoAccept" />
    <x-wirekit::time-picker name="opt-reject" label="Refused — the old time comes back, and the refusal is spoken" value="09:30" optimistic="demoReject" />
    <x-wirekit::time-picker name="opt-slow" label="Slow answer — the dashed outline is the provisional state" value="09:30" optimistic="demoSlow" />
</x-wirekit::stack>
</livewire:demos.optimistic-host>
:::

:::source{language="blade"}
{{-- In your app there is no host: your own Livewire component owns the method. --}}
<x-wirekit::time-picker
    name="start"
    :value="$start"
    optimistic="setStart"
/>
:::

The `<livewire:demos.…>` wrapper above exists only on this site — it supplies the demo
methods so the page can show a real round trip. The block under it is what you write.

The same shape as the other native form controls: the browser has already changed the value by the time any handler runs, so what this adds is the confirmation, the undo, and the announcement.

**What a screen reader hears** is the same for every component that supports this, and it is written out in full — one hedged announcement at the change, silence on confirmation, one more only if the server refuses, and focus that never moves — in [Optimistic UI — the announcement contract](/extending/optimistic-ui). That page is also where the boundary is stated: this covers mutations, not sorting, filtering or pagination.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `optimistic` | `string\|null` | `null` | Livewire method to call, showing the new value before the server confirms it. See [Optimistic UI](#optimistic-ui). |
| `optimisticArgs` | `array` | `[]` | Extra arguments appended to the optimistic action call, after the new value — the row this control belongs to. |
| `label` | `string\|null` | `null` | Label text above the input |
| `hint` | `string\|null` | `null` | Help text below the input |
| `error` | `string\|null` | `null` | Error message (also reads from `$errors`) |
| `name` | `string\|null` | `null` | Form field name |
| `id` | `string\|null` | auto-generated | Element id |
| `value` | `string\|null` | `null` | Initial time in `HH:MM` format |
| `format` | `string` | `'24h'` | `'24h'` or `'12h'` (display hint) |
| `step` | `int\|null` | `null` | Step interval in seconds |
| `size` | `string` | `'md'` | `'sm'`, `'md'`, `'lg'` |
| `disabled` | `bool` | `false` | Disabled state |
| `required` | `bool` | `false` | Required field |
| `scope` | `string\|null` | `null` | Scoped personalization key |

## Accessibility

- Native `<input type="time">` — full browser a11y and keyboard support
- `aria-invalid="true"` and `aria-describedby` set on error
- `aria-required="true"` when required
- Hint text linked via `aria-describedby`
- Focus ring visible via `focus-visible` styling
- Arrow keys increment/decrement hours and minutes (native behavior)
- The value is always submitted in ISO `HH:MM` format regardless of locale display

## Keyboard Interaction

| Key | Action |
|-----|--------|
| `Tab` | Move focus into the time input |
| Type `HH:MM` characters | Set the value directly |
| `ArrowUp` / `ArrowDown` | Step the focused field by ±1 |

## Pitfalls

- **Don't use time-picker for durations.** Native `<input type="time">` represents a clock time, not "2 hours 30 minutes". For durations use two `<x-wirekit::number-input>` (hours / minutes).

## Design Tokens

| Token | Purpose |
| --- | --- |
| `--color-wk-bg-input` | Input background |
| `--color-wk-border-strong` | Default border color |
| `--color-wk-border-error` | Border color on error |
| `--color-wk-text` | Input text color |
| `--color-wk-text-placeholder` | Placeholder text color |
| `--color-wk-ring` | Focus ring color |
| `--size-wk-sm` / `md` / `lg` | Input height per size variant |
| `--radius-wk-md` | Border radius |
| `--padding-wk-x-md` | Horizontal padding |
| `--text-wk-md` | Font size |
| `--transition-wk-duration` | Focus/error transition speed |
| `--opacity-wk-disabled` | Dimmed state when disabled |

## Customization

Override defaults in `config/wirekit.php`:

```php
'components' => [
    'time-picker' => ['size' => 'md', 'format' => '24h'],
],
```

## Further Reading

- [MDN: `<input type="time">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/time)
- [MDN: Date and time input types](https://developer.mozilla.org/en-US/docs/Learn/Forms/HTML5_input_types#date_and_time_pickers)
- [Can I Use: `input type=time`](https://caniuse.com/input-datetime) — browser support matrix
- [MDN: `step` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/step)
- [MDN: `aria-invalid`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-invalid)
