Usage
Use the v-model directive to control the selected date.
| 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 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const value = shallowRef(new CalendarDate(2022, 2, 3))
</script>
<template>
<PCalendar v-model="value" />
</template>
Use the default-value prop to set the initial value when you do not need to control its state.
| 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 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const defaultValue = shallowRef(new CalendarDate(2022, 2, 6))
</script>
<template>
<PCalendar :default-value="defaultValue" />
</template>
@internationalized/date package which provides objects and functions for representing and manipulating dates and times in a locale-aware manner.Multiple
Use the multiple prop to allow multiple selections.
| 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 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const value = shallowRef([
new CalendarDate(2022, 2, 4),
new CalendarDate(2022, 2, 6),
new CalendarDate(2022, 2, 8)
])
</script>
<template>
<PCalendar multiple v-model="value" />
</template>
Range
Use the range prop to select a range of dates.
| 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 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const value = shallowRef({
start: new CalendarDate(2022, 2, 3),
end: new CalendarDate(2022, 2, 20)
})
</script>
<template>
<PCalendar range v-model="value" />
</template>
Color
Use the color prop to change the color of the calendar.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
<template>
<PCalendar color="neutral" />
</template>
Variant
Use the variant prop to change the variant of the 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 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
<template>
<PCalendar variant="subtle" />
</template>
Size
Use the size prop to change the size of the calendar.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
<template>
<PCalendar size="xl" />
</template>
Disabled
Use the disabled prop to disable the calendar.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
<template>
<PCalendar disabled />
</template>
Number Of Months
Use the numberOfMonths prop to change the number of months in the calendar.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 | 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 |
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
29 | 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 |
<template>
<PCalendar :number-of-months="3" />
</template>
Month Controls
Use the month-controls prop to show the month controls. Defaults to true.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
<template>
<PCalendar :month-controls="false" />
</template>
Year Controls
Use the year-controls prop to show the year controls. Defaults to true.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
<template>
<PCalendar :year-controls="false" />
</template>
Fixed Weeks
Use the fixed-weeks prop to display the calendar with fixed weeks.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 |
<template>
<PCalendar :fixed-weeks="false" />
</template>
Examples
With chip events
Use the Chip component to add events to specific days.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date';
import { shallowRef } from 'vue';
const modelValue = shallowRef(new CalendarDate(2025, 10, 10));
function getColorByDate(date: Date) {
const isWeekend = date.getDay() % 6 == 0;
const isDayMeeting = date.getDay() % 3 == 0;
if (isWeekend) {
return undefined;
}
if (isDayMeeting) {
return 'error';
}
return 'success';
}
</script>
<template>
<PCalendar v-model="modelValue">
<template #day="{ day }">
<PChip
:show="!!getColorByDate(day.toDate('UTC'))"
:color="getColorByDate(day.toDate('UTC'))"
size="2xs"
>
{{ day.day }}
</PChip>
</template>
</PCalendar>
</template>
With disabled dates
Use the is-date-disabled prop with a function to mark specific dates as disabled.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
<script setup lang="ts">
import type { DateValue } from '@internationalized/date';
import { CalendarDate } from '@internationalized/date';
import { shallowRef } from 'vue';
const modelValue = shallowRef({
start: new CalendarDate(2025, 10, 1),
end: new CalendarDate(2025, 10, 9),
});
function isDateDisabled(date: DateValue) {
return date.day >= 10 && date.day <= 16;
}
</script>
<template>
<PCalendar
v-model="modelValue"
:is-date-disabled="isDateDisabled"
range
/>
</template>
With unavailable dates
Use the is-date-unavailable prop with a function to mark specific dates as unavailable.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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 | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
<script setup lang="ts">
import type { DateValue } from '@internationalized/date';
import { CalendarDate } from '@internationalized/date';
import { shallowRef } from 'vue';
const modelValue = shallowRef({
start: new CalendarDate(2025, 10, 1),
end: new CalendarDate(2025, 10, 9),
});
function isDateUnavailable(date: DateValue) {
return date.day >= 10 && date.day <= 16;
}
</script>
<template>
<PCalendar
v-model="modelValue"
:is-date-unavailable="isDateUnavailable"
range
/>
</template>
With min/max dates
Use the min-value and max-value props to limit the dates.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
27 | 28 | 29 | 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 |
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date';
import { shallowRef } from 'vue';
const modelValue = shallowRef(new CalendarDate(2023, 9, 10));
const minDate = new CalendarDate(2023, 9, 1);
const maxDate = new CalendarDate(2023, 9, 30);
</script>
<template>
<PCalendar
v-model="modelValue"
:min-value="minDate"
:max-value="maxDate"
/>
</template>
With other calendar systems
You can use other calendars from @internationalized/date to implement a different calendar system.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
24 | 25 | 26 | 27 | 28 | 29 | 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 |
<script lang="ts" setup>
import { CalendarDate, HebrewCalendar } from '@internationalized/date';
import { shallowRef } from 'vue';
const hebrewDate = shallowRef(new CalendarDate(new HebrewCalendar(), 5781, 1, 1));
</script>
<template>
<PCalendar v-model="hebrewDate" />
</template>
With external controls
You can control the calendar with external controls by manipulating the date passed in the v-model.
| 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 { CalendarDate } from '@internationalized/date';
import { shallowRef } from 'vue';
const date = shallowRef(new CalendarDate(2025, 4, 2));
</script>
<template>
<div class="flex flex-col gap-4">
<PCalendar
v-model="date"
:month-controls="false"
:year-controls="false"
/>
<div class="flex gap-4 justify-between">
<PButton
color="neutral"
variant="outline"
@click="date = date.subtract({ months: 1 })"
>
Prev
</PButton>
<PButton
color="neutral"
variant="outline"
@click="date = date.add({ months: 1 })"
>
Next
</PButton>
</div>
</div>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
as | 'div' | anyThe element or component this component should render as. |
nextYearIcon | appConfig.pohon.icons.chevronDoubleRight | string | objectThe icon to use for the next year control. |
nextYear | Omit<PButtonProps, PLinkPropsKeys>Configure the next year button.
| |
nextMonthIcon | appConfig.pohon.icons.chevronRight | string | objectThe icon to use for the next month control. |
nextMonth | Omit<PButtonProps, PLinkPropsKeys>Configure the next month button.
| |
prevYearIcon | appConfig.pohon.icons.chevronDoubleLeft | string | objectThe icon to use for the previous year control. |
prevYear | Omit<PButtonProps, PLinkPropsKeys>Configure the prev year button.
| |
prevMonthIcon | appConfig.pohon.icons.chevronLeft | string | objectThe icon to use for the previous month control. |
prevMonth | Omit<PButtonProps, PLinkPropsKeys>Configure the prev month button.
| |
color | 'primary' | "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral" |
variant | 'solid' | "solid" | "outline" | "soft" | "subtle" |
size | 'md' | "md" | "xs" | "sm" | "lg" | "xl" |
range | RWhether or not a range of dates can be selected | |
multiple | MWhether or not multiple dates can be selected | |
monthControls | true | boolean Show month controls |
yearControls | true | boolean Show year controls |
defaultValue | CalendarDate | CalendarDateTime | ZonedDateTime | DateRange | DateValue[] | |
modelValue | null | CalendarDate | CalendarDateTime | ZonedDateTime | DateRange | DateValue[] | |
defaultPlaceholder | CalendarDate | CalendarDateTime | ZonedDateTime | |
placeholder | CalendarDate | CalendarDateTime | ZonedDateTime | |
allowNonContiguousRanges | boolean When combined with | |
pagedNavigation | boolean This property causes the previous and next buttons to navigate by the number of months displayed at once, rather than one month | |
preventDeselect | boolean Whether or not to prevent the user from deselecting a date without selecting another date first | |
maximumDays | numberThe maximum number of days that can be selected in a range | |
weekStartsOn | 0 | 1 | 2 | 5 | 4 | 3 | 6The day of the week to start the calendar on | |
weekdayFormat | "long" | "narrow" | "short"The format to use for the weekday strings provided via the weekdays slot prop | |
fixedWeeks | true | boolean Whether or not to always display 6 weeks in the calendar |
maxValue | CalendarDate | CalendarDateTime | ZonedDateTime | |
minValue | CalendarDate | CalendarDateTime | ZonedDateTime | |
numberOfMonths | numberThe number of months to display at once | |
disabled | boolean Whether or not the calendar is disabled | |
readonly | boolean Whether or not the calendar is readonly | |
initialFocus | boolean If true, the calendar will focus the selected day, today, or the first day of the month depending on what is visible when the calendar is mounted | |
isDateDisabled | (date: DateValue): booleanA function that returns whether or not a date is disabled | |
isDateUnavailable | (date: DateValue): booleanA function that returns whether or not a date is unavailable | |
isDateHighlightable | (date: DateValue): booleanA function that returns whether or not a date is hightable | |
nextPage | (placeholder: DateValue): DateValueA function that returns the next page of the calendar. It receives the current placeholder as an argument inside the component. | |
prevPage | (placeholder: DateValue): DateValueA function that returns the previous page of the calendar. It receives the current placeholder as an argument inside the component. | |
disableDaysOutsideCurrentView | boolean Whether or not to disable days outside the current view. | |
fixedDate | "start" | "end"Which part of the range should be fixed | |
pohon | { root?: ClassValue; header?: ClassValue; body?: ClassValue; heading?: ClassValue; grid?: ClassValue; gridRow?: ClassValue; gridWeekDaysRow?: ClassValue; gridBody?: ClassValue; headCell?: ClassValue; cell?: ClassValue; cellTrigger?: ClassValue; } |
Slots
| Slot | Type |
|---|---|
heading | { value: string; } |
day | Pick<ACalendarCellTriggerProps, "day"> |
week-day | { day: string; } |
Emits
| Event | Type |
|---|---|
update:modelValue | [date: CalendarModelValue<R, M>] |
update:placeholder | [date: DateValue] & [date: DateValue] |
update:validModelValue | [date: DateRange] |
update:startValue | [date: DateValue | undefined] |
Theme
Below is the theme configuration skeleton for the PCalendar. Since the component is provided unstyled by default, you will need to fill in these values to apply your own custom look and feel. If you prefer to use our pre-built, opinionated styling, you can instead use our UnoCSS preset, this docs is using it as well.
export default defineAppConfig({
pohon: {
calendar: {
slots: {
root: '',
header: '',
body: '',
heading: '',
grid: '',
gridRow: '',
gridWeekDaysRow: '',
gridBody: '',
headCell: '',
cell: '',
cellTrigger: ''
},
variants: {
color: {
primary: {
headCell: '',
cellTrigger: ''
},
secondary: {
headCell: '',
cellTrigger: ''
},
success: {
headCell: '',
cellTrigger: ''
},
info: {
headCell: '',
cellTrigger: ''
},
warning: {
headCell: '',
cellTrigger: ''
},
error: {
headCell: '',
cellTrigger: ''
},
neutral: {
headCell: '',
cellTrigger: ''
}
},
variant: {
solid: '',
outline: '',
soft: '',
subtle: ''
},
size: {
xs: {
heading: '',
cell: '',
headCell: '',
cellTrigger: '',
body: ''
},
sm: {
heading: '',
headCell: '',
cell: '',
cellTrigger: ''
},
md: {
heading: '',
headCell: '',
cell: '',
cellTrigger: ''
},
lg: {
heading: '',
headCell: '',
cellTrigger: ''
},
xl: {
heading: '',
headCell: '',
cellTrigger: ''
}
}
},
defaultVariants: {
size: 'md',
color: 'primary',
variant: 'solid'
}
}
}
};
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import pohon from 'pohon-ui/vite'
export default defineAppConfig({
pohon: {
calendar: {
slots: {
root: '',
header: '',
body: '',
heading: '',
grid: '',
gridRow: '',
gridWeekDaysRow: '',
gridBody: '',
headCell: '',
cell: '',
cellTrigger: ''
},
variants: {
color: {
primary: {
headCell: '',
cellTrigger: ''
},
secondary: {
headCell: '',
cellTrigger: ''
},
success: {
headCell: '',
cellTrigger: ''
},
info: {
headCell: '',
cellTrigger: ''
},
warning: {
headCell: '',
cellTrigger: ''
},
error: {
headCell: '',
cellTrigger: ''
},
neutral: {
headCell: '',
cellTrigger: ''
}
},
variant: {
solid: '',
outline: '',
soft: '',
subtle: ''
},
size: {
xs: {
heading: '',
cell: '',
headCell: '',
cellTrigger: '',
body: ''
},
sm: {
heading: '',
headCell: '',
cell: '',
cellTrigger: ''
},
md: {
heading: '',
headCell: '',
cell: '',
cellTrigger: ''
},
lg: {
heading: '',
headCell: '',
cellTrigger: ''
},
xl: {
heading: '',
headCell: '',
cellTrigger: ''
}
}
},
defaultVariants: {
size: 'md',
color: 'primary',
variant: 'solid'
}
}
}
};
Akar
With Pohon UI, you can achieve similar component functionality with less code and effort, as it comes with built-in styles mechanism and behaviors that are optimized for common use cases. Since it's using unocss-variants it adds a runtime cost, but it can be worth it if you prioritize development speed and ease of use over fine-grained control.
If this is a deal breaker for you, you can always stick to using Akar and build your own custom components on top of it.