Skip to content
RTL Support: Full
Accessibility: Full
Translations: Not Needed

Time Picker

A component for selecting time with optional timezone and AM/PM support.

Basic Usage

Basic time picker with 24-hour format:

HH:mm:ss
vue
<template>
  <HLTimePicker v-model:value="time" format="HH:mm:ss" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(null)
</script>

Selection Behavior

When you click a column in the panel, the picker sets that column and resets every column you did not touch to 00. It never fills unselected columns from the current system time.

With Custom Prefix and Suffix

Using both prefix and suffix slots to add custom icons:

HH:mm:ss
vue
<template>
  <HLTimePicker v-model:value="time" format="HH:mm:ss">
    <template #prefix>
      <HLIcon>
        <CalendarIcon />
      </HLIcon>
    </template>
    <template #suffix>
      <HLIcon>
        <InfoCircleIcon />
      </HLIcon>
    </template>
  </HLTimePicker>
</template>

<script setup lang="ts">
import { HLTimePicker, HLIcon } from '@platform-ui/highrise'
import { InfoCircleIcon, CalendarIcon } from '@gohighlevel/ghl-icons/24/outline'
import { ref } from 'vue'

const time = ref(null)
</script>

With AM/PM and Timezone

The time picker provides independent am/pm and timezone selectors that emit respective values. Timezones should be passed as an array of objects with the following structure:

typescript
interface HLTimezone {
  label: string // Display name shown to users
  value: string // Timezone identifier (IANA timezone or custom)
  default?: boolean // Set to true for the default selected timezone
}
// IANA timezone identifiers
const timezones = [
  { label: 'Eastern Time', value: 'America/New_York', default: true },
  { label: 'Central Time', value: 'America/Chicago' },
  { label: 'Mountain Time', value: 'America/Denver' },
  { label: 'Pacific Time', value: 'America/Los_Angeles' },
  { label: 'UTC', value: 'UTC' },
]

WARNING

The time picker does not automatically adjust the displayed time when timezone or AM/PM changes. This gives you full control to implement your own timezone conversion and formatting logic.

hh:mm A
Select timezone
vue
<template>
  <HLTimePicker
    v-model:value="time"
    format="hh:mm a"
    :showAMPM="true"
    :timezones="timezones"
    @update:value="handleTimeChange"
    @update:ampm="handleAmPmChange"
    @update:timezone="handleTimezoneChange"
  />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'
import { timezones } from './options'

const time = ref(null)
const selectedAmPm = ref('AM')
const selectedTimezone = ref(null)

// Handle independent value changes
const handleTimeChange = timeValue => {
  time.value = timeValue
  // Apply your timezone conversion logic here
  console.log('Time:', timeValue, 'AM/PM:', selectedAmPm.value, 'Timezone:', selectedTimezone.value)
}

const handleAmPmChange = ampm => {
  selectedAmPm.value = ampm
  // Apply your 12-hour conversion logic here
}

const handleTimezoneChange = timezone => {
  selectedTimezone.value = timezone
  // Apply your timezone conversion logic here
}
</script>
ts
export const timezones = [
  { label: 'UTC', value: 'UTC' },
  { label: 'America/New_York', value: 'America/New_York' },
  { label: 'America/Chicago', value: 'America/Chicago' },
  { label: 'America/Denver', value: 'America/Denver' },
  { label: 'America/Los_Angeles', value: 'America/Los_Angeles' },
]

Format

format is a date-fns format string. It does double duty: it decides which columns the panel shows and how the time is displayed and emitted.

Supported tokens

TokenMeaningExample output
HHHours, 24-hour, zero-padded (0023)18
HHours, 24-hour, no padding (023)18
hhHours, 12-hour, zero-padded (0112)06
hHours, 12-hour, no padding (112)6
mmMinutes, zero-padded (0059)01
mMinutes, no padding1
ssSeconds, zero-padded (0059)00
sSeconds, no padding0
aAM/PM markerPM

Which columns appear follows directly from the tokens present:

  • an H or h shows the hours column
  • an m shows the minutes column
  • an s shows the seconds column
  • an h (lowercase) makes the hour column count 112 instead of 023

Separators are free-form — :, ., or spaces all work, and the same characters come back in the emitted string.

Column combinations

Each picker below is seeded with the same time (18:01:00) so you can compare how the format changes both the input text and the panel columns.

vue
<template>
  <!-- Hours, minutes, seconds -->
  <HLTimePicker v-model:value="time" format="HH:mm:ss" />
  <!-- Hours and minutes only — no seconds column -->
  <HLTimePicker v-model:value="time" format="HH:mm" />
  <!-- Hours only -->
  <HLTimePicker v-model:value="time" format="HH" />
  <!-- Minutes and seconds, no hours column (e.g. a duration) -->
  <HLTimePicker v-model:value="time" format="mm:ss" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(1183135260000)
</script>

12-hour vs 24-hour

A lowercase h switches the hour column to 12-hour counting. Uppercase H keeps it at 24-hour.

vue
<template>
  <!-- 24-hour: the hour column runs 00–23 -->
  <HLTimePicker v-model:value="time" format="HH:mm" />
  <!-- 12-hour: the hour column runs 01–12 -->
  <HLTimePicker v-model:value="time" format="hh:mm" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(1183135260000)
</script>

How format and showAMPM interact

There are two independent ways to bring AM/PM into the picker, and they produce different UI:

SetupAM/PM selector shown?Where AM/PM appearsEmitted formatted-value
format="hh:mm"NoNowhere — ambiguous06:01
format="hh:mm a"YesA separate dropdown beside the input06:01 PM
format="hh:mm" + :showAMPM="true"YesA separate dropdown beside the input06:01
format="hh:mm a" + :showAMPM="true"YesDropdown only — the a is stripped from the input text06:01 PM

Two rules explain the table:

  1. An a in format turns the selector on by itself. You do not have to set showAMPM — the component treats a format containing a as AM/PM mode.
  2. When showAMPM is true, the a token is removed from the text shown in the input, because the dropdown is already displaying it. The a is still honoured in the value emitted by @update:formatted-value, so your data keeps the marker either way.
vue
<template>
  <!-- `a` alone: the AM/PM dropdown appears without setting showAMPM -->
  <HLTimePicker v-model:value="time" format="hh:mm a" />

  <!-- showAMPM alone: dropdown appears, formatted value has no marker -->
  <HLTimePicker v-model:value="time" format="hh:mm" :showAMPM="true" />

  <!-- Both: dropdown appears, `a` is stripped from the input text but kept in the emitted value -->
  <HLTimePicker v-model:value="time" format="hh:mm a" :showAMPM="true" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(1183135260000)
</script>

INFO

Changing the AM/PM dropdown rewrites the underlying 24-hour value — picking PM on 06:01 produces 18:01, and the component re-emits @update:value with the new timestamp. It does not merely relabel the display.

WARNING

:showAMPM="true" with a 24-hour format (HH) is contradictory: the hour column still counts 0023, while the dropdown tries to force the hour into a 12-hour half. Selecting AM on 18:01 rewrites it to 06:01. Use a lowercase h whenever the AM/PM selector is visible.

Seeing the emitted value

format also determines the string emitted by @update:formatted-value, while @update:value always emits a plain millisecond timestamp regardless of format. Pick a time below to see both.

@update:value (ms): 1183135260000
@update:formatted-value: —
vue
<template>
  <HLTimePicker
    v-model:value="time"
    format="hh:mm:ss a"
    :showAMPM="true"
    @update:formatted-value="val => (formatted = val)"
  />
  <div>
    <div>@update:value (ms): {{ time }}</div>
    <div>@update:formatted-value: {{ formatted }}</div>
  </div>
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(1183135260000)
const formatted = ref(null)
</script>

INFO

The placeholder is derived from format when you don't pass placeholder.time — single tokens are doubled (HHH) and A is appended in AM/PM mode, so H:m yields the hint HH:mm A.

With Shortcuts

Time picker with predefined time shortcuts:

HH:mm:ss
vue
<template>
  <HLTimePicker v-model:value="time" :shortcuts="shortcuts" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'
import { shortcuts } from './options'

const time = ref(null)
</script>
ts
export const shortcuts = {
  Now: () => Date.now(),
  'Start of Day': () => {
    const date = new Date()
    date.setHours(0, 0, 0, 0)
    return date.getTime()
  },
  'End of Day': () => {
    const date = new Date()
    date.setHours(23, 59, 59, 999)
    return date.getTime()
  },
}

Without CTA (Action Buttons)

Time picker without confirm and clear buttons:

HH:mm:ss
vue
<template>
  <HLTimePicker v-model:value="time" :showCTA="false" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(null)
</script>

Auto-Close on Selection

Enable auto-close to automatically close the time picker panel after the last required time component is selected. This is useful for quick time selection workflows.

INFO

Auto-close only works when showCTA is false. When CTA buttons are visible, users must explicitly click confirm or clear.

HH:mm
vue
<template>
  <!-- Auto-closes after selecting minutes (last required column) -->
  <HLTimePicker v-model:value="time" :showCTA="false" :autoClose="true" format="HH:mm" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(null)
</script>

With Form Validation

Time picker with error state and validation message:

HH:mm:ss
vue
<template>
  <HLFormItem label="Time" validation-status="error" feedback="Please enter a valid time">
    <HLTimePicker v-model:value="time" status="error">
      <template #suffix>
        <HLIcon color="var(--error-600)">
          <InfoCircleIcon />
        </HLIcon>
      </template>
    </HLTimePicker>
  </HLFormItem>
</template>

<script setup lang="ts">
import { HLTimePicker, HLFormItem, HLIcon } from '@platform-ui/highrise'
import { InfoCircleIcon } from '@gohighlevel/ghl-icons/24/outline'
import { ref } from 'vue'

const time = ref(null)
</script>

With Custom Placeholders

Time picker with custom placeholder text for time, timezone, and AM/PM selectors:

Enter time
Choose your timezone
vue
<template>
  <HLTimePicker
    v-model:value="time"
    :showAMPM="true"
    :timezones="timezones"
    :placeholder="{
      time: 'Enter time',
      timezone: 'Choose your timezone',
    }"
  />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'
import { timezones } from './options'

const time = ref(null)
</script>
ts
export const timezones = [
  { label: 'UTC', value: 'UTC' },
  { label: 'America/New_York', value: 'America/New_York' },
  { label: 'America/Chicago', value: 'America/Chicago' },
  { label: 'America/Denver', value: 'America/Denver' },
  { label: 'America/Los_Angeles', value: 'America/Los_Angeles' },
]

Disabling Time

You can restrict time selection by disabling certain hours or minutes or seconds.

When a disabled time is selected, the component will automatically fall back to the first available enabled time and emit an update:fallback event describing the requested and applied values.

Business hours only (9 AM - 5 PM)
vue
<template>
  <HLTimePicker
    v-model:value="time"
    format="HH:mm:ss"
    :is-hour-disabled="(hour) => hour < 9 || hour > 17"
    :placeholder="{ time: 'Business hours only (9 AM - 5 PM)' }"
  />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(null)
</script>

Disable Minutes and Seconds

isMinuteDisabled and isSecondDisabled restrict minutes and seconds. Both receive the current higher-order selection, so you can make the rules depend on the chosen hour (and minute). Here minutes are limited to quarter-hour marks and seconds to 0.

Quarter-hour marks only
vue
<template>
  <HLTimePicker
    v-model:value="time"
    format="HH:mm:ss"
    :is-minute-disabled="(minute) => minute % 15 !== 0"
    :is-second-disabled="(second) => second !== 0"
    :placeholder="{ time: 'Quarter-hour marks only' }"
  />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(null)
</script>

Sizes

Set size to scale the input; it accepts lg, md, sm, xs, 2xs, and 3xs.

HH:mm
HH:mm
HH:mm
HH:mm
HH:mm
HH:mm
vue
<template>
  <HLTimePicker v-model:value="time" size="lg" format="HH:mm" />
  <HLTimePicker v-model:value="time" size="md" format="HH:mm" />
  <HLTimePicker v-model:value="time" size="sm" format="HH:mm" />
  <HLTimePicker v-model:value="time" size="xs" format="HH:mm" />
  <HLTimePicker v-model:value="time" size="2xs" format="HH:mm" />
  <HLTimePicker v-model:value="time" size="3xs" format="HH:mm" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(null)
</script>

Disabled

Pass disabled as a boolean to disable the whole component, or as an object ({ time, timezone, ampm }) to disable only specific parts.

Select timezone
Select timezone
vue
<template>
  <!-- Fully disabled -->
  <HLTimePicker v-model:value="time" format="hh:mm a" :showAMPM="true" :timezones="timezones" :disabled="true" />

  <!-- Only the timezone and AM/PM selectors disabled; time input stays editable -->
  <HLTimePicker v-model:value="time" format="hh:mm a" :showAMPM="true" :timezones="timezones" :disabled="{ timezone: true, ampm: true }" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'
import { timezones } from './options'

const time = ref(1183135260000)
</script>

Default Value

defaultValue (milliseconds) sets the time the picker resets to when cleared, rather than emptying entirely.

vue
<template>
  <HLTimePicker v-model:value="time" :defaultValue="defaultTime" format="HH:mm:ss" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(null)
// 2007-06-29 18:01:00 in ms
const defaultTime = 1183135260000
</script>

Custom Widths

Use timeInputWidth and ampmSelectWidth to override the auto-sized widths of the time input and the AM/PM selector.

hh:mm A
vue
<template>
  <HLTimePicker
    v-model:value="time"
    format="hh:mm a"
    :showAMPM="true"
    :timeInputWidth="200"
    :ampmSelectWidth="100"
  />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(null)
</script>

Teleport Target

Use to to control where the popover panel mounts. Pass a CSS selector or element to teleport it into a specific container, or false to render it inline (useful inside scrolling or overflow-hidden containers).

HH:mm:ss
vue
<template>
  <!-- Render the panel inline instead of teleporting to <body> -->
  <HLTimePicker v-model:value="time" format="HH:mm:ss" :to="false" />
</template>

<script setup lang="ts">
import { HLTimePicker } from '@platform-ui/highrise'
import { ref } from 'vue'

const time = ref(null)
</script>

Event Testing

This example logs the events the time picker emits as you interact with it. Try the following:

  • Pick a time to test @update:value and @update:formatted-value
  • Click confirm to test @update:confirm, or clear to test @update:clear
  • Change AM/PM or timezone to test @update:ampm and @update:timezone
  • Select a disabled hour to trigger @update:fallback
hh:mm:ss A
Select timezone

Event Log:

No events logged yet. Interact with the picker above.
vue
<template>
  <HLTimePicker
    v-model:value="time"
    format="hh:mm:ss a"
    :showAMPM="true"
    :timezones="timezones"
    :is-hour-disabled="(hour) => hour < 9 || hour > 17"
    @update:value="val => addEventLog('@update:value → ' + val)"
    @update:formatted-value="val => addEventLog('@update:formatted-value → ' + val)"
    @update:confirm="val => addEventLog('@update:confirm → ' + val)"
    @update:clear="addEventLog('@update:clear')"
    @update:ampm="val => addEventLog('@update:ampm → ' + val)"
    @update:timezone="tz => addEventLog('@update:timezone → ' + (tz ? tz.value : 'null'))"
    @update:fallback="info => addEventLog('@update:fallback → ' + info.reason)"
  />
  <div class="text-sm">
    <p class="font-bold mb-2">Event Log:</p>
    <div v-if="eventLog.length === 0" class="text-gray-500">No events logged yet. Interact with the picker above.</div>
    <div v-for="(log, index) in eventLog" :key="index" class="text-gray-700">{{ log.timestamp }}: {{ log.event }}</div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { HLTimePicker } from '@platform-ui/highrise'
import { timezones } from './options'

const time = ref(null)
const eventLog = ref<{ event: string; timestamp: string }[]>([])
const addEventLog = (event: string) => {
  eventLog.value.unshift({ event, timestamp: new Date().toLocaleTimeString() })
  if (eventLog.value.length > 5) {
    eventLog.value.pop()
  }
}
</script>

Design Guidelines

Input components use a box-shadow to render their focus ring. Box-shadows render outside the element's bounds and may be clipped by any ancestor using overflow: hidden (e.g. Tab Panels or Dropdown Menus).

To prevent this, add a small gutter padding to the component's wrapper to ensure there is enough room for the focus ring to render without being cut off.

vue
<div class="p-[3px]">
  <!-- Your component here -->
</div>

Accessibility

  • Tie the input to its label via aria-labelledby / aria-label and surface format hints with aria-describedby.
  • Toggle aria-expanded / aria-controls on the trigger when the time panel opens, and flag the active option using aria-selected.
  • Announce programmatic time changes inside an aria-live="polite" region when the value updates automatically.

Props

PropTypeDefaultDescription
size'lg' | 'md' | 'sm' | 'xs' | '2xs' | '3xs''md'Size of the time picker input
formatstring'HH:mm:ss'Time format string (follows date-fns format)
showAMPMbooleanfalseWhether to show AM/PM selector
disabledboolean | TimePickerDisabledStatefalseDisable the entire component or specific parts
placeholderTimePickerPlaceholders{ time: 'Select time', timezone: 'Select timezone' }Placeholder text for inputs
timezonesHLTimezone[][]Array of timezone options
ampmSelectWidthstring | numberDetermined based on the input sizeCustom width for AM/PM selector
timeInputWidthstring | numberDetermined based on the input sizeCustom width for time input
showCTAbooleantrueWhether to show the confirm and clear buttons
autoClosebooleanfalseAuto-close panel after last time component selection. Only works when showCTA is false
shortcutsRecord<string, number | (() => number)>{}Predefined shortcuts for quick time selection
status'success' | 'error' | 'warning' | undefinedundefinedValidation status of the input
defaultValuenumber | undefinedundefinedDefault time value when input is cleared in milliseconds
valuenumber | nullnullValue set to the time picker in milliseconds
isHourDisabled(hour: number) => booleanundefinedFunction to determine if a specific hour should be disabled
isMinuteDisabled(minute: number, selectedHour?: number) => booleanundefinedFunction to determine if a specific minute should be disabled, optionally based on selected hour
isSecondDisabled(second: number, selectedHour?: number, selectedMinute?: number) => booleanundefinedFunction to determine if a specific second should be disabled, based on selected hour and minute
tostring | HTMLElement | falseundefinedTeleport target for the time picker popover. Pass a CSS selector or HTMLElement to mount the panel inside a specific container. Pass false to disable teleporting.

Interfaces

TimePickerDisabledState Interface

typescript
interface TimePickerDisabledState {
  time?: boolean // Disable time input
  timezone?: boolean // Disable timezone selector
  ampm?: boolean // Disable AM/PM selector
}

TimePickerPlaceholders Interface

typescript
interface TimePickerPlaceholders {
  time?: string // Placeholder for time input
  timezone?: string // Placeholder for timezone selector
}

HLTimezone Interface (for timezones)

typescript
interface HLTimezone {
  label: string // Display label for the timezone
  value: string // Timezone value (e.g., 'America/Los_Angeles')
  default?: boolean // Whether this timezone is the default selection
}

Slots

NameParametersDescription
prefix-Content to be placed before the time input
suffix-Content to be placed after the time input

Emits

EventArgumentsDescription
update:value(value: number | null) => voidEmitted when time value changes
update:formatted-value(value: string | null) => voidEmitted when formatted time string changes
update:clear() => voidEmitted when time is cleared
update:confirm(value: number | null) => voidEmitted when time is confirmed with selected value
update:ampm(value: 'AM' | 'PM') => voidEmitted when AM/PM selection changes
update:timezone(timezone: HLTimezone | null) => voidEmitted when timezone selection changes
update:fallback(fallbackInfo: HLTimePickerFallbackInfo) => voidEmitted when the timepicker falls back to a valid time

Methods

MethodArgumentsDescription
focus() => voidFocus the time picker input
blur() => voidRemove focus from the time picker input