Time Field
Dates & Timeimport { TimeField } from "bits-ui" Bound time
A Time value with minute steps, a 12-hour cycle, and external 15-minute controls
<script lang="ts">
import { TimeField } from 'bits-ui';
import { Time } from '@internationalized/date';
let value = $state(new Time(9, 30));
function adjust(minutes: number) {
value = value.add({ minutes });
}
</script>
<div class="mx-auto grid w-full max-w-md gap-4">
<TimeField.Root bind:value granularity="minute" hourCycle={12} locale="en-US">
<div class="grid gap-2">
<TimeField.Label class="text-sm font-medium text-ink-200">Daily stand-up</TimeField.Label>
<TimeField.Input
name="stand-up-time"
class="input flex min-h-11 items-center focus-within:border-brand-600 data-invalid:border-accent-500"
>
{#snippet children({ segments })}
{#each segments as { part, value: segmentValue }, i (part + i)}
{#if part === 'literal'}
<TimeField.Segment {part} class="px-0.5 text-ink-600">
{segmentValue}
</TimeField.Segment>
{:else}
<TimeField.Segment
{part}
class="rounded-md px-1.5 py-1 font-mono text-sm tabular-nums outline-none hover:bg-ink-850 focus:bg-brand-500 focus:text-ink-950 data-invalid:text-accent-500 aria-[valuetext=Empty]:text-ink-600"
>
{segmentValue}
</TimeField.Segment>
{/if}
{/each}
{/snippet}
</TimeField.Input>
</div>
</TimeField.Root>
<div class="flex flex-wrap items-center gap-2">
<button type="button" class="btn btn-sm" onclick={() => adjust(-15)}>−15 min</button>
<button type="button" class="btn btn-sm" onclick={() => adjust(15)}>+15 min</button>
<span class="chip ml-auto font-mono">{value.toString()}</span>
</div>
</div>
Value type and precision
Time and CalendarDateTime values rendered with different granularities and hour cycles
Time · second precision · 24-hour
14:07:35CalendarDateTime · minute precision · 12-hour
2026-10-21T18:45:00<script lang="ts">
import { TimeField } from 'bits-ui';
import { CalendarDateTime, Time } from '@internationalized/date';
let preciseTime = $state(new Time(14, 7, 35));
let scheduledTime = $state(new CalendarDateTime(2026, 10, 21, 18, 45));
</script>
<div class="grid gap-5 sm:grid-cols-2">
<div class="grid content-start gap-2">
<TimeField.Root
bind:value={preciseTime}
granularity="second"
hourCycle={24}
locale="en-GB"
>
<TimeField.Label class="text-sm font-medium text-ink-200">Render deadline</TimeField.Label>
<TimeField.Input
name="render-deadline"
class="input flex min-h-11 items-center focus-within:border-brand-600 data-invalid:border-accent-500"
>
{#snippet children({ segments })}
{#each segments as { part, value: segmentValue }, i (part + i)}
{#if part === 'literal'}
<TimeField.Segment {part} class="px-0.5 text-ink-600">
{segmentValue}
</TimeField.Segment>
{:else}
<TimeField.Segment
{part}
class="rounded-md px-1 py-1 font-mono text-sm tabular-nums outline-none hover:bg-ink-850 focus:bg-brand-500 focus:text-ink-950 data-invalid:text-accent-500 aria-[valuetext=Empty]:text-ink-600"
>
{segmentValue}
</TimeField.Segment>
{/if}
{/each}
{/snippet}
</TimeField.Input>
</TimeField.Root>
<p class="muted text-xs">Time · second precision · 24-hour</p>
<code class="text-xs text-brand-400">{preciseTime.toString()}</code>
</div>
<div class="grid content-start gap-2">
<TimeField.Root
bind:value={scheduledTime}
granularity="minute"
hourCycle={12}
locale="en-US"
>
<TimeField.Label class="text-sm font-medium text-ink-200">Release call</TimeField.Label>
<TimeField.Input
name="release-call"
class="input flex min-h-11 items-center focus-within:border-brand-600 data-invalid:border-accent-500"
>
{#snippet children({ segments })}
{#each segments as { part, value: segmentValue }, i (part + i)}
{#if part === 'literal'}
<TimeField.Segment {part} class="px-0.5 text-ink-600">
{segmentValue}
</TimeField.Segment>
{:else}
<TimeField.Segment
{part}
class="rounded-md px-1 py-1 font-mono text-sm tabular-nums outline-none hover:bg-ink-850 focus:bg-brand-500 focus:text-ink-950 data-invalid:text-accent-500 aria-[valuetext=Empty]:text-ink-600"
>
{segmentValue}
</TimeField.Segment>
{/if}
{/each}
{/snippet}
</TimeField.Input>
</TimeField.Root>
<p class="muted text-xs">CalendarDateTime · minute precision · 12-hour</p>
<code class="text-xs text-brand-400">{scheduledTime.toString()}</code>
</div>
</div>
value may be a Time, CalendarDateTime, or ZonedDateTime, and the bound type is inferred from the supplied value. A CalendarDateTime keeps its date even though Time Field renders only its time segments. Supplying a name to TimeField.Input adds the hidden input used for form submission.
granularity is visual: hour, minute, or second determines the last rendered segment and defaults to minute. Locale changes segment ordering, separators, and the preferred clock; hourCycle explicitly overrides that preference. The placeholder is the starting time used when cycling an empty segment, not display-only hint text.