Shadcn-native calendar primitives

Scheduling primitives for shadcn apps that need real interaction.

CalendarCN gives you a reusable scheduler with month, week, day, and agenda views, recurring events, resource lanes, drag-and-drop editing, resize handles, and typed callbacks you can wire into your own state and persistence.

Quick start

Install the calendar directly into your app

shell
$npx shadcn@latest add https://calendarcn.phantomtechind.com/r/calendarcn.json

Use the hosted registry item for the fastest install path. Clone the repo only when you want the full docs site, showcase, and reference app.

Exports

Composable building blocks for shipping scheduling UI.

CalendarCN is structured as a reusable library. Use the full scheduler shell or pull in the toolbar, individual views, types, and slots where they fit.

CalendarRoot

The orchestrator for the full schedule

It coordinates visible range, recurrence expansion, selection, drag and resize interactions, and view rendering from one controlled API.

Use it when you want a complete scheduler surface without moving persistence or business rules into the component.

CalendarToolbar

Navigation wired to calendar state

Previous, next, today, and view switching already line up with the calendar API, so you are not rebuilding basic controls around every embed.

Keep the default toolbar or restyle it to match the surrounding app shell.

Views

Month, week, day, and agenda

Each view reads the same event and resource contracts, so you can change density and planning mode without changing your data layer.

That makes it practical to ship a broad planner, a focused day view, and an agenda feed from one model.

Types + slots

Typed contracts and slot-level customization

Events, resources, recurrences, and mutation payloads are typed, and the component exposes className slots for the key calendar surfaces.

You keep the interaction model, then adapt the styling and rendering to your own product.

Core behavior

Built for schedules that keep changing.

A useful calendar has to do more than render boxes. CalendarCN ships the interactions and state contracts you need once users start moving work around.

One contract across every view

Month, week, day, and agenda all consume the same event shape, so your scheduling data does not fork as the UI changes.

Create, move, and resize in place

Drag across open time to create a block, move events between slots and days, and resize directly from the edge where the schedule is visible.

Recurring events and resource-aware planning

Events can repeat, resources can define parallel lanes, and the same occurrence model drives selection, editing, and follow-up actions.

Fits the token system you already use

The calendar inherits the same shadcn color tokens and exposes className slots, so it can sit inside an existing app instead of forcing a separate visual system.

Integration model

Own the state. Let the calendar handle the surface.

CalendarCN is designed as a controlled component. Your app keeps the source of truth for date, view, events, and persistence; the calendar turns that into visible occurrences and interaction callbacks.

Controlled setup

1import { CalendarRoot, type CalendarEvent } from "@/components/calendar"
2
3const [events, setEvents] = useState<CalendarEvent[]>(seed)
4const [date, setDate] = useState(new Date())
5const [view, setView] = useState("week")
6const blockedRanges = [{ id: "lunch", start, end }]
7
8<CalendarRoot
9 availableViews={["week", "day", "agenda"]}
10 hiddenDays={[0, 6]}
11 businessHours={[{ days: [1, 2, 3, 4, 5], start: "09:00", end: "18:00" }]}
12 blockedRanges={blockedRanges}
13 density="compact"
14 date={date}
15 events={events}
16 hourCycle={24}
17 locale="en-GB"
18 scrollToTime="08:30"
19 view={view}
20 onDateChange={setDate}
21 onViewChange={setView}
22/>
1

Keep date, view, and events in your app

`useCalendarController` in the demo is only an example. In production, those values can live in React state, a server cache, or whatever store already owns scheduling logic.

2

CalendarRoot expands data into visible occurrences

It computes the visible range, expands recurring events for that range, renders the active view, and coordinates drag overlays, selection, keyboard moves, and accessibility announcements.

3

Views stay interchangeable because the model stays stable

Switching from a month grid to a time grid or agenda feed does not require a second contract. The same events and resources keep working across every exported view.

4

Mutations come back as typed operations

Create, move, resize, duplicate, archive, and delete actions can flow back through handlers you control, so your app decides how changes are validated, persisted, and synchronized.

Live demo

Same calendar behavior, different theme treatment.

Switch the site theme from the navbar and the calendar follows along. Layout, event behavior, and view logic stay the same while the tokens around them change.

Demo configuration

Switch presets or toggle the props live.

The demo is no longer pinned to one hardcoded setup. Choose a preset or flip individual options to see how the same `CalendarRoot` surface behaves under different configuration shapes.

Options

Active props

density="compact"hourCycle={24}locale="en-GB"scrollToTime="08:30"availableViews={["week", "day", "agenda"]}hiddenDays={[0, 6]}businessHours={demoBusinessHours}blockedRanges={demoBlockedRanges}

23 Mar - 27, 2026

Europe/Bucharest timezone · Secondary America/New_York · 3/3 calendars

All calendars active
Time

Mon

23 Mar

Tue

24 Mar

Wed

25 Mar

Thu

26 Mar

Today

Fri

27 Mar

All day

Use arrow keys to move the active time slot. Use left and right arrows to change days. Press Enter or Space to create an event at the active slot.

08

02

09

03

10

04

11

05

12

06

13

07

14

08

15

09

16

10

17

11

18

12

19

13

20

14

21

15

22

16

23

17

00

18

Lunch
Release freeze