TimePicker
Lets users select a specific time from a list or enter a custom value. Supports 12/24-hour formats, custom intervals, and optional time ranges.
Basic
<script setup lang="ts">
import { ref } from 'vue'
import { TimePicker } from 'frappe-ui'
// "When should we sync?" — typical meeting scheduler time slot
const meetingTime = ref('14:30')
</script>
<template>
<div class="flex w-72 flex-col gap-1.5">
<span class="text-sm text-ink-gray-7">Meeting time</span>
<TimePicker
v-model="meetingTime"
placeholder="Pick a time"
side="bottom"
align="start"
>
<template #prefix>
<span class="lucide-clock size-4 text-ink-gray-5" />
</template>
</TimePicker>
<span class="text-xs text-ink-gray-5">
Selected: {{ meetingTime || '—' }}
</span>
</div>
</template>24 Hour Format
<script setup lang="ts">
import { ref } from 'vue'
import { TimePicker } from 'frappe-ui'
// Schedule a nightly cron — 24h format keeps the ops team unambiguous
const cronTime = ref('02:00')
</script>
<template>
<div class="flex w-72 flex-col gap-1.5">
<span class="text-sm text-ink-gray-7">Nightly backup runs at</span>
<TimePicker
v-model="cronTime"
:use12Hour="false"
:interval="30"
>
<template #prefix>
<span class="lucide-server-cog size-4 text-ink-gray-5" />
</template>
</TimePicker>
<span class="text-xs text-ink-gray-5">UTC, every day at {{ cronTime }}</span>
</div>
</template>Custom Options
<script setup lang="ts">
import { ref } from 'vue'
import { TimePicker } from 'frappe-ui'
// A yoga studio offers only its scheduled class times — not free-form intervals
const classTime = ref('07:00')
const classOptions = [
{ value: '06:00', label: '6:00 AM — Sunrise Flow' },
{ value: '07:00', label: '7:00 AM — Vinyasa' },
{ value: '09:30', label: '9:30 AM — Gentle Hatha' },
{ value: '12:15', label: '12:15 PM — Lunch Power' },
{ value: '17:30', label: '5:30 PM — Restorative' },
{ value: '19:00', label: '7:00 PM — Candlelight Yin' },
]
</script>
<template>
<div class="flex w-80 flex-col gap-1.5">
<span class="text-sm text-ink-gray-7">Book a class</span>
<TimePicker v-model="classTime" :options="classOptions">
<template #prefix>
<span class="lucide-dumbbell size-4 text-ink-gray-5" />
</template>
</TimePicker>
</div>
</template>Min / Max Range
<script setup lang="ts">
import { ref } from 'vue'
import { TimePicker } from 'frappe-ui'
// Shift scheduling — limit each picker to business hours.
const shiftStart = ref('09:00')
const shiftEnd = ref('17:00')
</script>
<template>
<div class="flex w-full max-w-md flex-col gap-3">
<span class="text-sm font-medium text-ink-gray-7">Shift hours</span>
<div class="grid grid-cols-2 gap-3">
<div class="flex flex-col gap-1.5">
<span class="text-xs text-ink-gray-6">From</span>
<TimePicker
v-model="shiftStart"
min="06:00"
:max="shiftEnd"
:interval="15"
>
<template #prefix>
<span class="lucide-sunrise size-4 text-ink-gray-5" />
</template>
</TimePicker>
</div>
<div class="flex flex-col gap-1.5">
<span class="text-xs text-ink-gray-6">To</span>
<TimePicker
v-model="shiftEnd"
:min="shiftStart"
max="22:00"
:interval="15"
>
<template #prefix>
<span class="lucide-sunset size-4 text-ink-gray-5" />
</template>
</TimePicker>
</div>
</div>
<span class="text-xs text-ink-gray-5">
Working {{ shiftStart }} – {{ shiftEnd }}
</span>
</div>
</template>API Reference
Show types
/**
* TimePicker is a thin wrapper around Combobox that adds time-aware parsing,
* generated options, and a 12/24-hour display toggle. Canonical value is
* always 24-hour `HH:mm` (or `HH:mm:ss` if seconds were typed).
*/
export type PopoverSide = 'top' | 'right' | 'bottom' | 'left'
export type PopoverAlign = 'start' | 'center' | 'end'
export type Placement =
| 'bottom-start'
| 'top-start'
| 'top-end'
| 'bottom-end'
| 'right-start'
| 'right-end'
| 'left-start'
| 'left-end'
export type Variant = 'outline' | 'subtle'
export interface TimePickerProps {
/** Controlled value, canonical `HH:mm` (or `HH:mm:ss`). */
modelValue?: string
/**
* Uncontrolled initial value.
* @deprecated Use `modelValue` with `v-model` instead.
*/
value?: string
/** Minute interval between generated options. */
interval?: number
/** Caller-provided option values; bypasses the generated grid. */
options?: Array<{ value: string; label?: string }>
/** Preferred popover side. */
side?: PopoverSide
/** Alignment of the popover along the trigger edge. */
align?: PopoverAlign
/** Gap between trigger and popover in pixels. */
offset?: number
/**
* Combined side+align placement.
* @deprecated Use `side` and `align` instead.
*/
placement?: Placement
/** Placeholder text when no value is selected. */
placeholder?: string
/** Visual style variant. */
variant?: Variant
/**
* Whether the trigger input accepts typed input. When `false` the user can
* still open the popover and pick a time, but cannot type a time manually.
* Default: `true`.
*/
typeable?: boolean
/**
* Prevents manual typing while keeping the picker interactive.
* @deprecated Use `typeable: false` instead.
*/
readonly?: boolean
/**
* Allows users to type custom time values.
* @deprecated Use `typeable: false` instead.
*/
allowCustom?: boolean
/** Keeps the popover open after a time is selected. */
keepOpen?: boolean
/**
* Closes the popover after a value is picked.
* @deprecated Use `keepOpen` (inverse semantics).
*/
autoClose?: boolean
/** Use 12-hour (am/pm) format for display. */
use12Hour?: boolean
/** Disable the time picker. */
disabled?: boolean
/** Controlled popover open state. Use with `v-model:open` for two-way binding. */
open?: boolean
/** Opens the popover when the input receives focus. Default: false. */
openOnFocus?: boolean
/** Opens the popover when the input is clicked. Default: true. */
openOnClick?: boolean
/** Minimum selectable time as `HH:mm[:ss]`. */
min?: string
/** Maximum selectable time as `HH:mm[:ss]`. */
max?: string
/**
* Minimum selectable time as `HH:mm[:ss]`.
* @deprecated Use `min` instead.
*/
minTime?: string
/**
* Maximum selectable time as `HH:mm[:ss]`.
* @deprecated Use `max` instead.
*/
maxTime?: string
/**
* Scroll behavior when opening the list.
* @deprecated Scrolling is always centered now.
*/
scrollMode?: 'center' | 'start' | 'nearest'
}
export type TimePickerEmits = {
(e: 'update:modelValue', value: string): void
(e: 'update:open', value: boolean): void
(e: 'change', value: string): void
(e: 'input-invalid', input: string): void
(e: 'invalid-change', invalid: boolean): void
(e: 'open'): void
(e: 'close'): void
}Controlled value, canonical `HH:mm` (or `HH:mm:ss`).
Deprecated — Use `modelValue` with `v-model` instead.
Minute interval between generated options.
Caller-provided option values; bypasses the generated grid.
Preferred popover side.
Alignment of the popover along the trigger edge.
Gap between trigger and popover in pixels.
Deprecated — Use `side` and `align` instead.
Placeholder text when no value is selected.
Visual style variant.
Whether the trigger input accepts typed input. When `false` the user can still open the popover and pick a time, but cannot type a time manually. Default: `true`.
Deprecated — Use `typeable: false` instead.
Deprecated — Use `typeable: false` instead.
Keeps the popover open after a time is selected.
Deprecated — Use `keepOpen` (inverse semantics).
Use 12-hour (am/pm) format for display.
Disable the time picker.
Controlled popover open state. Use with `v-model:open` for two-way binding.
Opens the popover when the input receives focus. Default: false.
Opens the popover when the input is clicked. Default: true.
Minimum selectable time as `HH:mm[:ss]`.
Maximum selectable time as `HH:mm[:ss]`.
Deprecated — Use `min` instead.
Deprecated — Use `max` instead.
Deprecated — Scrolling is always centered now.
| Slot | Payload |
|---|---|
prefix | — Rendered inside the trigger input, before the typed value. |
suffix | { togglePopover: () => void; isOpen: boolean; } Rendered inside the trigger input, after the typed value. Defaults to a chevron-down that toggles the popover. |
Rendered inside the trigger input, before the typed value.
Rendered inside the trigger input, after the typed value. Defaults to a chevron-down that toggles the popover.
| Event | Payload |
|---|---|
update:modelValue | [value: string] Fired when the model value changes. |
change | [value: string] Fired after the value is committed. |
open | [] Fired when the component opens. |
update:open | [value: boolean] Fired when the open state changes. |
close | [] Fired when the component closes. |
input-invalid | [input: string] |
invalid-change | [invalid: boolean] |
Fired when the model value changes.
Fired after the value is committed.
Fired when the component opens.
Fired when the open state changes.
Fired when the component closes.