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.

December 2025
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
9
10
Event Date, December 2025
<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.

December 2025
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
9
10
Event Date, December 2025
<template>
  <PCalendar size="xl" />
</template>

Disabled

Use the disabled prop to disable the calendar.

December 2025
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
9
10
Event Date, December 2025
<template>
  <PCalendar disabled />
</template>

Number Of Months

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

December 2025 - February 2026
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
9
10
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
31
1
2
3
4
5
6
7
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, December 2025 - February 2026
<template>
  <PCalendar :number-of-months="3" />
</template>

Month Controls

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

December 2025
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
9
10
Event Date, December 2025
<template>
  <PCalendar />
</template>

Year Controls

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

December 2025
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
9
10
Event Date, December 2025
<template>
  <PCalendar />
</template>

Fixed Weeks

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

December 2025
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
Event Date, December 2025
<template>
  <PCalendar />
</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 calenders 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

Slots

Slot Type

Emits

Event Type

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