Calendar

Displays dates and days of the week, facilitating date-related interactions.

Usage

Use the v-model directive to control the selected date.

February 2022
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
Event Date, February 2022
<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.

February 2022
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
Event Date, February 2022
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'

const defaultValue = shallowRef(new CalendarDate(2022, 2, 6))
</script>

<template>
  <PCalendar :default-value="defaultValue" />
</template>
This component relies on the @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.

February 2022
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
Event Date, February 2022
<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.

Event Date, February 2022
February 2022
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.

February 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Event Date, February 2026
<template>
  <PCalendar color="neutral" />
</template>

Variant

Use the variant prop to change the variant of the calendar.

Event Date, February 2022
February 2022
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.

February 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Event Date, February 2026
<template>
  <PCalendar size="xl" />
</template>

Disabled

Use the disabled prop to disable the calendar.

February 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Event Date, February 2026
<template>
  <PCalendar disabled />
</template>

Number Of Months

Use the numberOfMonths prop to change the number of months in the calendar.

February - April 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
Event Date, February - April 2026
<template>
  <PCalendar :number-of-months="3" />
</template>

Month Controls

Use the month-controls prop to show the month controls. Defaults to true.

February 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Event Date, February 2026
<template>
  <PCalendar :month-controls="false" />
</template>

Year Controls

Use the year-controls prop to show the year controls. Defaults to true.

February 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Event Date, February 2026
<template>
  <PCalendar :year-controls="false" />
</template>

Fixed Weeks

Use the fixed-weeks prop to display the calendar with fixed weeks.

February 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
Event Date, February 2026
<template>
  <PCalendar :fixed-weeks="false" />
</template>

Examples

With chip events

Use the Chip component to add events to specific days.

October 2025
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
Event Date, October 2025
<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.

Event Date, October 2025
October 2025
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.

Event Date, October 2025
October 2025
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.

September 2023
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
Event Date, September 2023
<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.

Tishri 5781
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
Event Date, Tishri 5781
<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>
You can check all the available calendars on @internationalized/date docs.

With external controls

You can control the calendar with external controls by manipulating the date passed in the v-model.

April 2025
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
Event Date, April 2025
<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'any

The element or component this component should render as.

nextYearIconappConfig.pohon.icons.chevronDoubleRight string | object

The icon to use for the next year control.

nextYear Omit<PButtonProps, PLinkPropsKeys>

Configure the next year button. { color: 'neutral', variant: 'ghost' }

nextMonthIconappConfig.pohon.icons.chevronRight string | object

The icon to use for the next month control.

nextMonth Omit<PButtonProps, PLinkPropsKeys>

Configure the next month button. { color: 'neutral', variant: 'ghost' }

prevYearIconappConfig.pohon.icons.chevronDoubleLeft string | object

The icon to use for the previous year control.

prevYear Omit<PButtonProps, PLinkPropsKeys>

Configure the prev year button. { color: 'neutral', variant: 'ghost' }

prevMonthIconappConfig.pohon.icons.chevronLeft string | object

The icon to use for the previous month control.

prevMonth Omit<PButtonProps, PLinkPropsKeys>

Configure the prev month button. { color: 'neutral', variant: 'ghost' }

color'primary' "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral"
variant'solid' "solid" | "outline" | "soft" | "subtle"
size'md' "md" | "xs" | "sm" | "lg" | "xl"
range R

Whether or not a range of dates can be selected

multiple M

Whether or not multiple dates can be selected

monthControlstrueboolean

Show month controls

yearControlstrueboolean

Show year controls

defaultValueCalendarDate | CalendarDateTime | ZonedDateTime | DateRange | DateValue[]
modelValuenull | CalendarDate | CalendarDateTime | ZonedDateTime | DateRange | DateValue[]
defaultPlaceholderCalendarDate | CalendarDateTime | ZonedDateTime
placeholderCalendarDate | CalendarDateTime | ZonedDateTime
allowNonContiguousRangesboolean

When combined with isDateUnavailable, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected.

pagedNavigationboolean

This property causes the previous and next buttons to navigate by the number of months displayed at once, rather than one month

preventDeselectboolean

Whether or not to prevent the user from deselecting a date without selecting another date first

maximumDays number

The maximum number of days that can be selected in a range

weekStartsOn 0 | 1 | 2 | 5 | 4 | 3 | 6

The 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

fixedWeekstrueboolean

Whether or not to always display 6 weeks in the calendar

maxValueCalendarDate | CalendarDateTime | ZonedDateTime
minValueCalendarDate | CalendarDateTime | ZonedDateTime
numberOfMonths number

The number of months to display at once

disabledboolean

Whether or not the calendar is disabled

readonlyboolean

Whether or not the calendar is readonly

initialFocusboolean

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): boolean

A function that returns whether or not a date is disabled

isDateUnavailable (date: DateValue): boolean

A function that returns whether or not a date is unavailable

isDateHighlightable (date: DateValue): boolean

A function that returns whether or not a date is hightable

nextPage (placeholder: DateValue): DateValue

A function that returns the next page of the calendar. It receives the current placeholder as an argument inside the component.

prevPage (placeholder: DateValue): DateValue

A function that returns the previous page of the calendar. It receives the current placeholder as an argument inside the component.

disableDaysOutsideCurrentViewboolean

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; }
dayPick<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

We use unocss-variants to customize the theme. Read more about it in the theming guide.

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.

app.config.ts
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'
      }
    }
  }
};
vite.config.ts
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.

Changelog

No recent changes