Date and Time Pickers
The UI kit has three date components. The admin uses <CwaUiDatePicker> for a component's scheduled publish time and a route's Goes live date, and you can use all three in your own admin tabs and pages. Like the rest of the kit, they're auto-imported with the CwaUi prefix. They're built on reka-ui and ported from Nuxt UI's Calendar and InputDate.
@cwa/nuxt 2.0.0-alpha.2.| Component | What it is | Value |
|---|---|---|
<CwaUiDatePicker> | A date and time field with a calendar popover | A UTC ISO string |
<CwaUiCalendar> | The calendar on its own | @internationalized/date values |
<CwaUiInputDate> | The segmented field on its own | @internationalized/date values |
Use <CwaUiDatePicker> when you're storing a date in the API. The other two are the parts it's built from, for when you need a date without a time, a range, or several dates.
CwaUiDatePicker
<script setup lang="ts">
const goLive = ref<string | null>(null)
const now = new Date().toISOString()
</script>
<template>
<CwaUiDatePicker v-model="goLive" label="Goes live" :min="now" />
</template>
The field has day, month, year, hour and minute segments, on a 24-hour clock. Editors type into a segment or step it with the arrow keys, or open the calendar with the button at the end of the field. Picking a day in the calendar keeps the time already set, or uses 09:00 if the field was empty.
| Prop | Type | Default | Description |
|---|---|---|---|
v-model | string | null | — | The chosen moment as a UTC ISO string, such as 2026-10-01T08:30:00.000Z. null shows an empty field. The picker never emits null, so clear the value yourself if you need to. |
label | string | — | Shown above the field and used as its accessible name. |
min | string | — | A UTC ISO string. Nothing earlier can be chosen or emitted: the earliest time is min rounded up to the next minuteStep, and an earlier value is replaced by it. |
minuteStep | number | 5 | How far the arrow keys move the minutes. |
locale | string | en-GB | Sets the order and format of the segments and the calendar's month names. |
disabled | boolean | — | Disables the field and the calendar button. |
Time Zones
The editor picks a time on their own computer's clock, and the picker converts it to UTC. Hovering the calendar button names the zone and its UTC offset: "Times are in Europe/London, UTC+01:00". The offset is the one at the chosen date, not today's, so a time on the other side of a clock change shows the offset that will apply then.
CwaUiCalendar
A calendar on its own. It uses the DateValue types from @internationalized/date. The module depends on it, but pnpm only lets your code import packages your app lists, so add it with the same major version (pnpm add @internationalized/date@^3) and keep to one copy, see Troubleshooting:
<script setup lang="ts">
import { today, getLocalTimeZone } from '@internationalized/date'
import type { DateValue } from '@internationalized/date'
const day = shallowRef<DateValue>()
const earliest = today(getLocalTimeZone())
</script>
<template>
<CwaUiCalendar v-model="day" :min-value="earliest" />
</template>
The heading switches between the day, month and year views, and the arrow buttons move by a month or a year.
| Prop | Type | Default | Description |
|---|---|---|---|
v-model | DateValue | DateValue[] | DateRange | null | — | A single date, an array with multiple, or { start, end } with range. |
range | boolean | — | Select a start and end date. |
multiple | boolean | — | Select several dates. Ignored with range. |
type | 'day' | 'month' | 'year' | day | What a selection picks: a day, a month or a year. The views above it are still available for navigation. |
minValue / maxValue | DateValue | — | The earliest and latest date that can be chosen. |
isDateDisabled | (date: DateValue) => boolean | — | Disables the dates it returns true for. |
isDateUnavailable | (date: DateValue) => boolean | — | Marks the dates it returns true for as unavailable. |
weekStartsOn | 0–6 | 1 (Monday) | The first day of the week, where 0 is Sunday. |
numberOfMonths | number | — | How many months to show side by side. |
fixedWeeks | boolean | true | Always shows six weeks, so the height doesn't change between months. |
locale | string | en-GB | Month and weekday names. |
monthControls / yearControls | boolean | true | Show the previous/next month and previous/next year buttons. |
viewControl | boolean | true | Let the heading switch to the month and year views. |
disabled / readonly | boolean | — | |
initialFocus | boolean | — | Focus the calendar when it mounts. |
CwaUiInputDate
The segmented field on its own, without the calendar:
<CwaUiInputDate v-model="day" granularity="day" />
| Prop | Type | Default | Description |
|---|---|---|---|
v-model | DateValue | DateRange | null | — | A single date, or { start, end } with range. |
range | boolean | — | Show a start and an end field. |
granularity | 'day' | 'hour' | 'minute' | 'second' | — | The smallest segment shown. |
hourCycle | 12 | 24 | — | The clock used for the hour segment. |
step | { year?, month?, day?, hour?, minute?, second? } | — | How far the arrow keys move each segment. |
minValue / maxValue | DateValue | — | The earliest and latest value. |
isDateUnavailable | (date: DateValue) => boolean | — | Marks dates as unavailable. |
locale | string | en-GB | Sets the order and format of the segments. |
hideTimeZone | boolean | true | Hides the time zone segment of a zoned value. |
disabled / readonly | boolean | — | |
id / name | string | — | Passed to the field. |
Anything in the default slot is shown at the end of the field. <CwaUiDatePicker> puts its calendar button there.
Troubleshooting: the Picker Shows No Time
If <CwaUiDatePicker> shows a date with no hour and minute, check the browser console for this warning:
[CWA] Two copies of @internationalized/date are installed, so reka-ui cannot read the date picker value and its time is not shown.
Your lockfile has two copies of @internationalized/date, so reka-ui doesn't recognise the date-time value the picker gives it. It can happen when your app already had reka-ui installed, for example through Nuxt UI, on an older version. Run this to keep one copy:
pnpm dedupe