| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 31 | 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 | 6 | 7 | 8 | 9 | 10 |
Value: 2026-09-04 to 2026-09-12
<script setup lang="ts">
import { ref } from 'vue'
import { RangeCalendar, Stack, Text, type DateRangeValue } from '@hina-ui/vue'
const range = ref<DateRangeValue | null>({ start: '2026-09-04', end: '2026-09-12' })
</script>
<template>
<Stack gap="sm" align="start">
<RangeCalendar v-model="range" />
<Text tone="muted" size="sm">
Value: {{ range ? `${range.start ?? 'empty'} to ${range.end ?? 'empty'}` : 'empty' }}
</Text>
</Stack>
</template>
Usage
import { RangeCalendar } from '@hina-ui/vue'
The range calendar shares its grid, heading and month and year views with Calendar; the difference is that it picks a span: click one date as the start, then another as the end. Start and end render as solid cells and the dates between them join into a tinted band. v-model binds a { start, end } object whose fields are YYYY-MM-DD ISO 8601 strings, the same shape as DateRangeField, and is null when nothing is selected.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 31 | 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 | 6 | 7 | 8 | 9 | 10 |
Value: empty
<script setup lang="ts">
import { ref } from 'vue'
import { RangeCalendar, Stack, Text, type DateRangeValue } from '@hina-ui/vue'
const range = ref<DateRangeValue | null>(null)
</script>
<template>
<Stack gap="sm" align="start">
<RangeCalendar v-model="range" placeholder="2026-09-01" />
<Text tone="muted" size="sm">
Value: {{ range ? `${range.start ?? 'empty'} to ${range.end ?? 'empty'}` : 'empty' }}
</Text>
</Stack>
</template>
Examples
Range
min and max bound the selectable range and disable the dates outside it. maximumDays caps how many days a span may contain: once a start date is picked, the dates beyond the cap are disabled until the span is complete.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 31 | 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 | 6 | 7 | 8 | 9 | 10 |
<script setup lang="ts">
import { ref } from 'vue'
import { RangeCalendar, type DateRangeValue } from '@hina-ui/vue'
const range = ref<DateRangeValue | null>({ start: '2026-09-10', end: '2026-09-14' })
</script>
<template>
<RangeCalendar v-model="range" min="2026-09-07" max="2026-09-25" :maximum-days="7" />
</template>
Sizes
size has three steps, sm, md and lg, identical to the same step of Calendar.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 31 | 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 | 6 | 7 | 8 | 9 | 10 |
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 31 | 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 | 6 | 7 | 8 | 9 | 10 |
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 31 | 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 | 6 | 7 | 8 | 9 | 10 |
<script setup lang="ts">
import { RangeCalendar, Stack } from '@hina-ui/vue'
const range = { start: '2026-09-04', end: '2026-09-12' }
</script>
<template>
<Stack gap="lg" align="start">
<RangeCalendar size="sm" :model-value="range" />
<RangeCalendar size="md" :model-value="range" />
<RangeCalendar size="lg" :model-value="range" />
</Stack>
</template>
States
readonly keeps the calendar navigable by keyboard but not selectable; disabled disables the whole calendar.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 31 | 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 | 6 | 7 | 8 | 9 | 10 |
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
30 | 31 | 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 | 6 | 7 | 8 | 9 | 10 |
<script setup lang="ts">
import { RangeCalendar, Inline } from '@hina-ui/vue'
const range = { start: '2026-09-04', end: '2026-09-12' }
</script>
<template>
<Inline gap="lg" align="start">
<RangeCalendar :model-value="range" readonly />
<RangeCalendar :model-value="range" disabled />
</Inline>
</template>
Behavior
- The first click sets the start date, at which point the value is
{ start, end: null }; the second click sets the end. Once a span is set, clicking any date starts a new span from it. - After the start is set, hovering or moving the focus to another date previews the band in between.
- An end before the start swaps the two.
- Keyboard handling, the month and year views, paging and
placeholderare the same asCalendar.
Accessibility
- The root carries the localized name and the current month, the grid is
role="grid"and every cell inside the span carriesaria-selected. - The whole grid is one Tab stop; the arrow keys move the focus between dates and the focus ring only appears for keyboard use.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | DateRangeValue | null | null | The selected span, both ends YYYY-MM-DD |
placeholder | string | This month | The month shown, supports v-model:placeholder |
min | string | — | Lower bound of the selectable range |
max | string | — | Upper bound of the selectable range |
maximumDays | number | — | Maximum number of days a span may cover |
unavailable | (date: string) => boolean | — | Whether a given date is unavailable |
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 six weeks are always shown |
size | 'sm' | 'md' | 'lg' | 'md' | Size |
autofocus | boolean | false | Whether to focus the start date on mount |
readonly | boolean | false | Whether the calendar is read only |
disabled | boolean | false | Whether the calendar is disabled |
class | string | — | Classes appended to the root element |
DateRangeValue is { start: string | null; end: string | null } and is exported from the package entry.
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | value: DateRangeValue | null | The span changed |
update:placeholder | value: string | The shown month changed, with a date inside the new view |