Components
HeroUI v3 Calendar — date calendar with year picker, controlled state, week/day views, min/max, unavailable dates, multiple selection, read-only, focused value, cell indicators, multiple months, booking example and custom styles.
npx @21st-dev/cli@beta add hero_ui/heroui-calendarLoading preview...
"use client"
import type { CalendarDate } from "@internationalized/date"
import { Button, ButtonGroup, Description } from "@heroui/react"
import {
getLocalTimeZone,
parseDate,
startOfMonth,
startOfWeek,
today,
} from "@internationalized/date"
import { useState } from "react"
import { I18nProvider, useLocale } from "react-aria-components"
import Calendar from "@/components/ui/heroui-calendar"
function ControlledInner() {
const [value, setValue] = useState<CalendarDate | null>(null)
const [focusedDate, setFocusedDate] = useState<CalendarDate>(parseDate("2025-12-25"))
const { locale } = useLocale()
return (
<div className="flex flex-col items-center gap-4">
<ButtonGroup fullWidth size="sm" variant="tertiary">
<Button
onPress={() => {
const todayDate = today(getLocalTimeZone())
setValue(todayDate)
setFocusedDate(todayDate)
}}
>
Today
</Button>
<Button
onPress={() => {
const nextWeekStart = startOfWeek(today(getLocalTimeZone()), locale)
setValue(nextWeekStart)
setFocusedDate(nextWeekStart)
}}
>
Week
</Button>
<Button
onPress={() => {
const nextMonthStart = startOfMonth(today(getLocalTimeZone()))
setValue(nextMonthStart)
setFocusedDate(nextMonthStart)
}}
>
Month
</Button>
</ButtonGroup>
<Calendar
aria-label="Event date"
focusedValue={focusedDate}
value={value}
onChange={setValue}
onFocusChange={setFocusedDate}
>
<Calendar.Header>
<Calendar.Heading />
<Calendar.NavButton slot="previous" />
<Calendar.NavButton slot="next" />
</Calendar.Header>
<Calendar.Grid>
<Calendar.GridHeader>
{(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}
</Calendar.GridHeader>
<Calendar.GridBody>{(date) => <Calendar.Cell date={date} />}</Calendar.GridBody>
</Calendar.Grid>
</Calendar>
<Description className="text-center">
Selected date: {value ? value.toString() : "(none)"}
</Description>
<div className="flex gap-2">
<Button
size="sm"
variant="secondary"
onPress={() => {
const todayDate = today(getLocalTimeZone())
setValue(todayDate)
setFocusedDate(todayDate)
}}
>
Set Today
</Button>
<Button
size="sm"
variant="secondary"
onPress={() => {
const christmasDate = parseDate("2025-12-25")
setValue(christmasDate)
setFocusedDate(christmasDate)
}}
>
Set Christmas
</Button>
<Button size="sm" variant="tertiary" onPress={() => setValue(null)}>
Clear
</Button>
</div>
</div>
)
}
export default function Controlled() {
return (
<I18nProvider locale="en-US">
<ControlledInner />
</I18nProvider>
)
}
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...