Skip to content
Kappa
Date Picker
@dicehub/kappav0.4.2

Date Picker

Selects a single date, multiple dates, or a range with an editable input and a calendar popover.

Selected: 2026-10-04
<script setup>
import { ref } from "vue";
import { DatePicker } from "@dicehub/kappa/components/date-picker";

const value = ref([]);
</script>

<template>
  <DatePicker.Root v-model="value" name="target-date">
    <DatePicker.Label>Target completion date</DatePicker.Label>
    <DatePicker.Control>
      <DatePicker.Input placeholder="Pick a date" />
      <DatePicker.ClearTrigger aria-label="Clear date" />
      <DatePicker.Trigger aria-label="Open calendar" />
    </DatePicker.Control>
    <DatePicker.Content>
      <DatePicker.Calendar />
    </DatePicker.Content>
  </DatePicker.Root>
</template>

Installation

Barrel

import {
  DatePicker,
  DatePickerRoot,
  DatePickerRootProvider,
  DatePickerLabel,
  DatePickerControl,
  DatePickerInput,
  DatePickerTrigger,
  DatePickerClearTrigger,
  DatePickerContent,
  DatePickerCalendar,
} from "@dicehub/kappa";

Granular

import {
  DatePicker,
  DatePickerRoot,
  DatePickerRootProvider,
  DatePickerLabel,
  DatePickerControl,
  DatePickerInput,
  DatePickerTrigger,
  DatePickerClearTrigger,
  DatePickerContent,
  DatePickerCalendar,
} from "@dicehub/kappa/components/date-picker";

Usage

A popover date picker needs a Control with an Input, aTrigger, and a Content holding the calendar. Values areDateValue objects from @internationalized/date; create them with CalendarDate, today(), or the exportedparseDate helper.

<script setup>
import { ref } from "vue";
import { DatePicker } from "@dicehub/kappa/components/date-picker";

const value = ref([]);
</script>

<template>
  <DatePicker.Root v-model="value" name="report-date">
    <DatePicker.Label>Report date</DatePicker.Label>
    <DatePicker.Control>
      <DatePicker.Input placeholder="YYYY-MM-DD" />
      <DatePicker.Trigger aria-label="Open calendar" />
    </DatePicker.Control>
    <DatePicker.Content>
      <DatePicker.Calendar />
    </DatePicker.Content>
  </DatePicker.Root>
</template>

Composition

Typical part hierarchy
DatePicker.Root
├── DatePicker.Label
├── DatePicker.Control
│   ├── DatePicker.Input
│   ├── DatePicker.ClearTrigger
│   └── DatePicker.Trigger
└── DatePicker.Content
    └── DatePicker.Calendar

Ark UI owns the calendar state machine, keyboard grid navigation, input parsing, visible-range logic, and the popover layer. Kappa owns the compound API, the composedCalendar part, default icons, dense geometry, states, and semantic tokens.

  • Root owns selection, view, open state, locale, and constraints.
  • Content teleports the popover to the body; pass :teleport="false" to render in place.
  • Calendar renders the full day view: prev/next navigation, month titles, weekday header, and day cells. Multi-month and week numbers come from Root.
  • Table parts stay public for fully custom grids; MonthSelect and YearSelect handle long jumps.

See the Ark UI Date Pickerguide for the primitive behavior contract. Month and year grid views are available through the View, ViewControl, and table parts; the composedCalendar intentionally ships the day view only.

<script setup>
import {
  DatePickerRoot,
  DatePickerControl,
  DatePickerInput,
  DatePickerTrigger,
  DatePickerContent,
  DatePickerCalendar,
} from "@dicehub/kappa/components/date-picker";
</script>

<template>
  <DatePickerRoot>
    <DatePickerControl>
      <DatePickerInput placeholder="Pick a date" />
      <DatePickerTrigger aria-label="Open calendar" />
    </DatePickerControl>
    <DatePickerContent>
      <DatePickerCalendar />
    </DatePickerContent>
  </DatePickerRoot>
</template>

Examples

Inline

With inline, the calendar renders in place without a popover.

October 2026
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<script setup>
import { ref } from "vue";
import { DatePicker } from "@dicehub/kappa/components/date-picker";

const value = ref([]);
</script>

<template>
  <DatePicker.Root v-model="value" inline>
    <DatePicker.Calendar />
  </DatePicker.Root>
</template>

Date Range

selectionMode="range" selects a start and an end. Two months keep the whole window visible while picking.

2026-10-07 → 2026-10-14
<script setup>
import { ref } from "vue";
import { DatePicker } from "@dicehub/kappa/components/date-picker";

const samplingWindow = ref([]);
</script>

<template>
  <DatePicker.Root v-model="samplingWindow" selection-mode="range" :num-of-months="2">
    <DatePicker.Label>Sampling window</DatePicker.Label>
    <DatePicker.Control>
      <DatePicker.Input placeholder="Start date" />
      <DatePicker.Trigger aria-label="Open calendar" />
    </DatePicker.Control>
    <DatePicker.Content>
      <DatePicker.Calendar />
    </DatePicker.Content>
  </DatePicker.Root>
</template>

Range Constraints

Limit the range length with isDateUnavailable. While a range start is pending — the controlled value holds one date — mark days too close or too far as unavailable. The pattern needs no wrapper state and keeps the Ark machine authoritative.

October 2026
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
November 2026
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
Ranges must span 2–6 days. Pick a start date, and days outside the limit become unavailable.
<script setup>
import { today, getLocalTimeZone } from "@internationalized/date";
import { computed, ref } from "vue";
import { DatePicker } from "@dicehub/kappa/components/date-picker";

const RANGE_MIN_DAYS = 2;
const RANGE_MAX_DAYS = 6;

const value = ref([today(getLocalTimeZone()).add({ days: 2 }), today(getLocalTimeZone()).add({ days: 5 })]);

// A pending range start: the controlled value holds exactly one date.
const pendingStart = computed(() => (value.value.length === 1 ? value.value[0] : undefined));

const outsideRangeLimits = (date) => {
  if (!pendingStart.value) return false;
  const days = Math.abs(
    Math.round((date.toDate("UTC") - pendingStart.value.toDate("UTC")) / 86_400_000),
  );
  return days < RANGE_MIN_DAYS || days > RANGE_MAX_DAYS;
};
</script>

<template>
  <DatePicker.Root
    v-model="value"
    selection-mode="range"
    inline
    :num-of-months="2"
    :is-date-unavailable="outsideRangeLimits"
  >
    <DatePicker.Calendar />
  </DatePicker.Root>
</template>

Presets

PresetTrigger applies a prepared value in one click.

Selected: 2026-10-04
<script setup>
import { CalendarDate, today, getLocalTimeZone } from "@internationalized/date";
import { ref } from "vue";
import { DatePicker } from "@dicehub/kappa/components/date-picker";

const value = ref([today(getLocalTimeZone())]);
const inDays = (days) => today(getLocalTimeZone()).add({ days });
</script>

<template>
  <DatePicker.Root v-model="value">
    <DatePicker.Control>
      <DatePicker.Input placeholder="Pick a date" />
      <DatePicker.Trigger aria-label="Open calendar" />
    </DatePicker.Control>
    <DatePicker.Content>
      <DatePicker.Calendar />
      <div class="presets">
        <DatePicker.PresetTrigger :value="[inDays(0)]">Today</DatePicker.PresetTrigger>
        <DatePicker.PresetTrigger :value="[inDays(7)]">In a week</DatePicker.PresetTrigger>
      </div>
    </DatePicker.Content>
  </DatePicker.Root>
</template>

Month and Year Selects

Native dropdowns for long jumps sit above the composed calendar.

<script setup>
import { ref } from "vue";
import { DatePicker } from "@dicehub/kappa/components/date-picker";

const value = ref([]);
</script>

<template>
  <DatePicker.Root v-model="value">
    <DatePicker.Control>
      <DatePicker.Input placeholder="Pick a date" />
      <DatePicker.Trigger aria-label="Open calendar" />
    </DatePicker.Control>
    <DatePicker.Content>
      <div class="selects">
        <DatePicker.MonthSelect aria-label="Month" />
        <DatePicker.YearSelect aria-label="Year" />
      </div>
      <DatePicker.Calendar />
    </DatePicker.Content>
  </DatePicker.Root>
</template>

Min, Max, and Unavailable

min and max bound the selectable range;isDateUnavailable blocks specific days such as weekends.

Weekdays only, within the next 30 days.
<script setup>
import { today, getLocalTimeZone } from "@internationalized/date";
import { ref } from "vue";
import { DatePicker } from "@dicehub/kappa/components/date-picker";

const value = ref([]);
const now = today(getLocalTimeZone());
const weekendUnavailable = (date) => {
  const day = new Date(date.year, date.month - 1, date.day).getDay();
  return day === 0 || day === 6;
};
</script>

<template>
  <DatePicker.Root
    v-model="value"
    :min="now"
    :max="now.add({ days: 30 })"
    :is-date-unavailable="weekendUnavailable"
  >
    <DatePicker.Label>Submission date</DatePicker.Label>
    <DatePicker.Control>
      <DatePicker.Input placeholder="Next 30 days, weekdays" />
      <DatePicker.Trigger aria-label="Open calendar" />
    </DatePicker.Control>
    <DatePicker.Content>
      <DatePicker.Calendar />
    </DatePicker.Content>
  </DatePicker.Root>
</template>

Week Numbers

showWeekNumbers adds the ISO week column to the day view.

October 2026
39
27
28
29
30
1
2
3
40
4
5
6
7
8
9
10
41
11
12
13
14
15
16
17
42
18
19
20
21
22
23
24
43
25
26
27
28
29
30
31
<script setup>
import { DatePicker } from "@dicehub/kappa/components/date-picker";
</script>

<template>
  <DatePicker.Root inline show-week-numbers>
    <DatePicker.Calendar />
  </DatePicker.Root>
</template>

Locale

locale localizes month names, weekday order, and input formatting.

<script setup>
import { ref } from "vue";
import { DatePicker } from "@dicehub/kappa/components/date-picker";

const value = ref([]);
</script>

<template>
  <DatePicker.Root v-model="value" locale="de-DE" name="abgabetermin">
    <DatePicker.Label>Abgabetermin</DatePicker.Label>
    <DatePicker.Control>
      <DatePicker.Input placeholder="Datum wählen" />
      <DatePicker.Trigger aria-label="Kalender öffnen" />
    </DatePicker.Control>
    <DatePicker.Content>
      <DatePicker.Calendar />
    </DatePicker.Content>
  </DatePicker.Root>
</template>

States

Disabled blocks the field; invalid marks the control and needs a text description.

Select a date before the review closes.

<script setup>
import { CalendarDate } from "@internationalized/date";
import { DatePicker } from "@dicehub/kappa/components/date-picker";
</script>

<template>
  <DatePicker.Root disabled :default-value="[new CalendarDate(2026, 8, 1)]">
    <DatePicker.Label>Disabled start date</DatePicker.Label>
    <DatePicker.Control>
      <DatePicker.Input placeholder="Unavailable" />
      <DatePicker.Trigger aria-label="Open calendar" />
    </DatePicker.Control>
  </DatePicker.Root>
  <DatePicker.Root invalid>
    <DatePicker.Label>Invalid milestone date</DatePicker.Label>
    <DatePicker.Control>
      <DatePicker.Input placeholder="Required" aria-describedby="milestone-error" />
      <DatePicker.Trigger aria-label="Open calendar" />
    </DatePicker.Control>
  </DatePicker.Root>
  <p id="milestone-error">Select a date before the review closes.</p>
</template>

Accessibility

  • The input is the primary control: type a date, or open the calendar with the trigger button.
  • Arrow keys move the focused date; PageUp and PageDown move between months; Home and End move within a week.
  • Every icon-only trigger needs an accessible name; the defaults document the pattern.
  • The month title announces changes through a polite live region.
  • Connect error text with aria-describedby on the input.
  • Unavailable dates stay focusable with an unavailable announcement, matching the primitive contract.
  • Keyboard focus remains visible on every interactive element.

API Reference

DatePicker.Root

PropTypeDefaultDescription
modelValueDateValue[]-Controlled selected dates; supports v-model.
defaultValueDateValue[][]Initially selected dates for uncontrolled use.
selectionMode"single" | "multiple" | "range""single"Selects one date, many dates, or a contiguous range.
inlinebooleanfalseRenders the calendar inline instead of inside a popover.
numOfMonthsnumber1Renders this many adjacent month grids.
startOfWeeknumber0First weekday column; 0 is Sunday, 1 is Monday.
showWeekNumbersbooleanfalseAdds the ISO week-number column to the day view.
fixedWeeksbooleanfalseAlways renders six weeks to keep the grid height stable.
minDateValue-Earliest selectable date.
maxDateValue-Latest selectable date.
isDateUnavailable(date, locale) => boolean-Marks specific dates unavailable, such as weekends or booked days.
localestring"en-US"BCP 47 tag driving month, weekday, and input formatting.
timeZonestring"UTC"Time zone used for date math and the today marker.
format(date, details) => stringlocalizedOverrides how the input renders the selected date.
parse(value, details) => DateValuelocalizedOverrides how typed text parses back into a date.
open / defaultOpenbooleanfalseControlled or initial popover open state; pair open with update:open.
openOnClickbooleanfalseOpens the calendar when the input is clicked.
closeOnSelectbooleantrueCloses the popover after a completed selection.
positioningPositioningOptionsbottom-startFloating placement, gutter, and viewport handling for the popover.
view / defaultView"day" | "month" | "year""day"Controlled or initial calendar view.
disabled / readOnly / required / invalidbooleanfalseStandard form states, exposed as data attributes.
namestring-Form field name for the hidden value.
maxSelectedDatesnumber-Caps selections in multiple mode.
createCalendar(identifier) => CalendarGregorianEnables non-Gregorian calendars such as Persian or Buddhist.

Parts

PartElementDescription
DatePicker.LabellabelNames the field and focuses the input on click.
DatePicker.ControldivVisual box grouping the input and icon triggers.
DatePicker.InputinputEditable date text; parses on commit.
DatePicker.TriggerbuttonOpens the popover; renders a calendar icon by default.
DatePicker.ClearTriggerbuttonClears the selection; renders an × icon by default.
DatePicker.ContentdivTeleported positioner and popover surface.
DatePicker.CalendardivComposed day-view calendar: navigation, month titles, weekday header, and day grid.
DatePicker.View / ViewControl / ViewTriggerdivView switching primitives for day, month, and year grids.
DatePicker.PrevTrigger / NextTriggerbuttonStep the visible range; render chevron icons by default.
DatePicker.Table … TableCellTriggertableFull grid anatomy for custom calendar layouts.
DatePicker.WeekNumberCell / WeekNumberHeaderCelltdISO week-number column cells.
DatePicker.ValueTextdivFormatted selected value outside the input.
DatePicker.RangeTextdivFormatted visible range text.
DatePicker.PresetTriggerbuttonApplies a prepared value such as Today or Next week.
DatePicker.MonthSelect / YearSelectselectNative month and year jump dropdowns.
DatePicker.ContextrenderlessExposes the date picker API to its slot.
DatePicker.RootProviderdivRoot driven by an external useDatePicker machine.

Events

EventPayloadDescription
update:modelValueDateValue[]Emitted on selection changes; drives v-model.
valueChangeDatePickerValueChangeDetailsArk UI detail with value and valueAsString.
update:openbooleanEmitted when the popover opens or closes; drives v-model:open.
openChange{ open: boolean }Ark UI detail for popover visibility.
focusChangeDatePickerFocusChangeDetailsEmitted when the focused date moves.
viewChangeDatePickerViewChangeDetailsEmitted when the calendar view changes.
visibleRangeChangeDatePickerVisibleRangeChangeDetailsEmitted when the visible month range changes.

Exports

ExportDescription
DatePickerCompound API exposing every named part.
DatePickerRootUnaugmented root state machine host.
DatePickerCalendarComposed day-view calendar with navigation.
DatePickerContentTeleported popover surface.
DatePickerPropsPublic root props and Ark UI state contract.
DatePickerEmitsRoot event contract.
DateValue@internationalized/date value type used across the API.
DatePickerValueChangeDetailsPayload for valueChange.
parseDateArk UI helper parsing ISO strings into DateValue objects.
useDatePickerArk UI machine hook for external state control.
datePickerAnatomyArk UI part anatomy metadata.