DatePicker

Types a date or picks it from a calendar.

9
4
2026

Value: 2026-09-04

<script setup lang="ts">
  import { ref } from 'vue'
  import { DatePicker, Stack, Text } from '@hina-ui/vue'

  const date = ref<string | null>('2026-09-04')
</script>

<template>
  <Stack gap="sm" align="start">
    <DatePicker v-model="date" clearable aria-label="Publish date" class="w-72" />
    <Text tone="muted" size="sm">Value: {{ date ?? 'empty' }}</Text>
  </Stack>
</template>
vue

Usage

import { DatePicker } from '@hina-ui/vue'
ts

The date picker puts the segmented DateField and the Calendar together: type into the segments, or open the calendar from the button at the end and pick a day. v-model binds an ISO 8601 string in the YYYY-MM-DD form, the same as both parts. Attributes it does not declare land on the group element that wraps the segments, so name it with aria-label or aria-labelledby.

mm
dd
yyyy

Value: empty

<script setup lang="ts">
  import { ref } from 'vue'
  import { DatePicker, Stack, Text } from '@hina-ui/vue'

  const date = ref<string | null>(null)
</script>

<template>
  <Stack gap="sm" align="start">
    <DatePicker v-model="date" aria-label="Release date" class="w-72" />
    <Text tone="muted" size="sm">Value: {{ date ?? 'empty' }}</Text>
  </Stack>
</template>
vue

Examples

Range and unavailable days

min and max constrain both the field and the calendar; unavailable strikes days out.

9
10
2026
<script setup lang="ts">
  import { ref } from 'vue'
  import { DatePicker } from '@hina-ui/vue'

  const date = ref<string | null>('2026-09-10')
  const weekend = (value: string) => [0, 6].includes(new Date(`${value}T00:00`).getDay())
</script>

<template>
  <DatePicker
    v-model="date"
    min="2026-09-01"
    max="2026-09-30"
    :unavailable="weekend"
    aria-label="Event date"
    class="w-72"
  />
</template>
vue

Clearable

clearable adds a clear button after the segments; clearing sets the value to null.

9
4
2026
<script setup lang="ts">
  import { ref } from 'vue'
  import { DatePicker } from '@hina-ui/vue'

  const date = ref<string | null>('2026-09-04')
</script>

<template>
  <DatePicker v-model="date" clearable aria-label="Publish date" class="w-72" />
</template>
vue

Sizes

size has three steps, sm, md and lg, matching the input of the same step; the calendar in the popover uses the same step.

9
4
2026
9
4
2026
9
4
2026
<script setup lang="ts">
  import { DatePicker, Stack } from '@hina-ui/vue'
</script>

<template>
  <Stack class="w-72">
    <DatePicker size="sm" model-value="2026-09-04" aria-label="Small" />
    <DatePicker size="md" model-value="2026-09-04" aria-label="Medium" />
    <DatePicker size="lg" model-value="2026-09-04" aria-label="Large" />
  </Stack>
</template>
vue

States

invalid gives the field a warning color, disabled disables the group and readonly makes it read only. variant="secondary" is the flat form used inside a surface.

9
4
2026
9
4
2026
9
4
2026
9
4
2026
<script setup lang="ts">
  import { DatePicker, Stack } from '@hina-ui/vue'
</script>

<template>
  <Stack class="w-72">
    <DatePicker model-value="2026-09-04" invalid aria-label="Invalid" />
    <DatePicker model-value="2026-09-04" disabled aria-label="Disabled" />
    <DatePicker model-value="2026-09-04" readonly aria-label="Read only" />
    <DatePicker model-value="2026-09-04" variant="secondary" aria-label="Secondary" />
  </Stack>
</template>
vue

In a form

Inside a FormField the label is linked to the input, and the description and error message are rendered by the field; validation rules and submission belong to the Form.

mm
dd
yyyy
<script setup lang="ts">
  import { reactive, ref } from 'vue'
  import * as v from 'valibot'
  import { Button, DatePicker, Form, FormField, Text } from '@hina-ui/vue'

  const schema = v.object({
    released: v.string('Pick a release date'),
  })

  const values = reactive({ released: null as string | null })
  const saved = ref('')

  async function save(data: unknown) {
    await new Promise(resolve => setTimeout(resolve, 600))
    saved.value = JSON.stringify(data)
  }
</script>

<template>
  <Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="save">
    <FormField name="released" label="Release date" required>
      <DatePicker v-model="values.released" />
    </FormField>
    <Button type="submit" :loading="submitting" class="self-start">Save</Button>
    <Text v-if="saved" tone="muted" size="sm">Saved: {{ saved }}</Text>
  </Form>
</template>
vue

Behavior

  • The button at the end opens the calendar with focus on the picked day; picking a day writes the value, closes the calendar and returns focus to the button.
  • The calendar is a modal popover: focus stays inside it while open, page scrolling is locked, and clicking anywhere outside it, the field included, closes it.
  • Esc closes the calendar and returns focus to the button; inside its month and year views Esc steps back first.
  • The calendar opens below the field aligned to its start edge.

Accessibility

  • The segments and the calendar carry the semantics described for DateField and Calendar.
  • The button that opens the calendar has a name from the locale and aria-expanded.
  • Name the group with aria-label or aria-labelledby.

API

Props

Prop
Type
Default
Description
modelValue
string | null
null
The picked day, YYYY-MM-DD
open
boolean
false
Whether the calendar is open; v-model:open
placeholder
string
today
Starting point of the empty field and calendar
min
string
—
Lower bound of the selectable range
max
string
—
Upper bound of the selectable range
unavailable
(date: string) => boolean
—
Whether a given day cannot be picked
weekStartsOn
0 | 1 | 2 | 3 | 4 | 5 | 6
locale
First day of the week
weekdayFormat
'narrow' | 'short'
'narrow'
Format of the weekday names
fixedWeeks
boolean
true
Whether the calendar always shows six weeks
clearable
boolean
false
Whether the clear button is shown
readonly
boolean
false
Whether the field is read only
name
string
—
Form field name
variant
'primary' | 'secondary'
'primary'
Variant
size
'sm' | 'md' | 'lg'
'md'
Size
disabled
boolean
false
Whether the group is disabled
invalid
boolean
false
Whether the field failed validation
class
string
—
Classes appended to the root element

Slots

Slot
Description
leading
Content of the leading adornment

Events

Event
Payload
Description
update:modelValue
value: string | null
The value changed
update:open
open: boolean
The calendar opened or closed
clear
—
The clear was used