Quick start
Build a week, day, month, and agenda calendar with the built-in views.
You'll build this calendar: a toolbar, a view switcher, and the built-in week, day, month, and agenda views, showing your own meetings.
To see these views running with more data before you start, open the live demo.
Install
Install @moltenship/calendar as described in Installation, including
Temporal if your browsers need it.
Copy the UI source
The views are source files that you copy into your app. They're built on shadcn/ui and its default Base UI primitives, so your app needs a Base UI style (see UI source). Add the primitives they use:
npx shadcn@latest add button popover select tooltipThen copy these folders from the repository's registry, and change their @registry/… imports
to your own paths, as UI source describes:
| Folder | Copy to | Contains |
|---|---|---|
calendar-theme | styles/, components/calendar/ | The theme CSS and event color names |
calendar | components/calendar/ | Toolbar, view switcher, event chip |
time-grid-view | components/calendar/time-grid-view/ | Week and day views |
month-view | components/calendar/month-view/ | Month view |
list-view | components/calendar/list-view/ | Agenda view |
Import calendar-theme.css in your global CSS, after the Tailwind and shadcn imports.
Create the calendar
Describe your records with an adapter, then create a calendar with the views you want. The first view in the list is shown first.
"use client";
import { createCalendar, defineEventAdapter } from "@moltenship/calendar";
import { CalendarToolbar } from "@/components/calendar/calendar-toolbar";
import type { CalendarEventColor } from "@/components/calendar/event-colors";
import { createAgendaView } from "@/components/calendar/list-view/agenda-view";
import { createMonthView } from "@/components/calendar/month-view/month-view";
import { createDayView, createWeekView } from "@/components/calendar/time-grid-view/time-grid-view";
import { ViewSwitcher } from "@/components/calendar/view-switcher";
export interface Meeting {
readonly id: string;
readonly title: string;
readonly start: Temporal.Instant;
readonly end: Temporal.Instant;
readonly color: CalendarEventColor;
}
export const eventAdapter = defineEventAdapter<Meeting>()({
getId: (meeting) => meeting.id,
getTitle: (meeting) => meeting.title,
getInterval: (meeting) => ({ kind: "timed", start: meeting.start, end: meeting.end }),
getResourceIds: () => [],
});
const getEventColor = (meeting: Meeting) => meeting.color;
export const Calendar = createCalendar<Meeting>()({
eventAdapter,
views: [
createWeekView<Meeting>({ dayView: "day", getEventColor }),
createDayView<Meeting>({ getEventColor }),
createMonthView<Meeting>({ getEventColor }),
createAgendaView<Meeting>({ getEventColor }),
],
});getEventColor picks each event's color from the palette.
dayView: "day" makes the dates in the week header open the day view.
Render it
Root holds the calendar state. It needs your events, a starting date, a time zone, a locale, and
week rules (the first day of the week, where 1 is Monday, and the weekend days).
CalendarToolbar adds the Today, previous, and next buttons and the title, and the registry's
ViewSwitcher switches between your views.
const viewLabels = { week: "Week", day: "Day", month: "Month", agenda: "Agenda" };
const viewSwitcherOptions = Calendar.views.map(({ name }) => ({
value: name,
label: viewLabels[name],
}));
function CalendarViewSwitcher() {
const view = Calendar.useCalendarSelector((state) => state.view);
const { setView } = Calendar.useCalendarActions();
return <ViewSwitcher value={view} onValueChange={setView} options={viewSwitcherOptions} />;
}
export const weekRules = { firstDay: 1, minimalDays: 4, weekend: [6, 7] } as const;
export function TeamCalendar({ meetings }: { readonly meetings: readonly Meeting[] }) {
return (
<Calendar.Root
events={meetings}
defaultAnchorDate={Temporal.PlainDate.from("2026-10-08")}
defaultTimeZone="Europe/Berlin"
locale="en-US"
weekRules={weekRules}
>
<Calendar.Frame className="flex h-[640px] flex-col rounded-xl border">
<CalendarToolbar calendar={Calendar}>
<CalendarViewSwitcher />
</CalendarToolbar>
<Calendar.Viewport className="min-h-0 flex-1">
<Calendar.ActiveView />
</Calendar.Viewport>
</Calendar.Frame>
</Calendar.Root>
);
}Give the frame a height: the views fill it and scroll inside it. The calendar is read-only until
you add an onEventChange handler; Interactions shows how to save dragged and
resized events.
The toolbar's buttons use shadcn's Tooltip, which shadcn sets up with a TooltipProvider around
your app. If your app doesn't have one yet, add it once:
import { TooltipProvider } from "@/components/ui/tooltip";
<TooltipProvider>
<TeamCalendar meetings={meetings} />
</TooltipProvider>;Using Next.js?
Calendar modules are Client Components, which is why the file starts with "use client". See
Next.js and server rendering for passing data from a Server Component.
Next steps
Last updated on