BuildCalendar
v1.0.23Updated 19 Oct 2026Month / week / day calendar with event chips and drag-to-create. Use when users need to see and schedule events on a grid.
Preview
Live · 19 stories▸Why this is recommendedcalendareventsschedulemonth+7
A polished showcase bringing several BuildCalendar variations together — beautiful.
- ✓Scheduling surfaces
- ✓Booking calendars
- ✓Editorial / publishing calendars
- ✓a calendar whose events are loaded per month from an API
- ✓showing who is on leave this month
- Month / week / day views
- Multi-day event spanning (an event with `end` appears on every intersecting day)
- Event chips with colour by category
- Drag-to-create new event
- Click event to open detail
- Today highlight + weekend tint
- All-day vs timed events
- Read-only friendly: create affordances render only when onCreateEvent is provided
- Server-backed: onRangeChange reports the visible period (start, end, view, referenceDate) on mount and on every change — chevrons, Today, the mini-calendar, a view switch and "+n more" — so the host can fetch exactly the days on screen. The month range covers the whole 42-cell grid, including the leading/trailing days of the neighbouring months, and it never fires twice for the same range
- Controlled period: referenceDate + onReferenceDateChange makes the toolbar report instead of moving itself; defaultReferenceDate keeps it self-navigating while still reporting. referenceDate alone behaves exactly as before
- getCalendarVisibleRange(referenceDate, view, weekStartsOn) is exported, so a host can compute the same range before it renders
- i18n (FB-18): labels prop overrides every built-in string; LocaleProvider drives localized month/weekday names, Buddhist-calendar headers and weekStartsOn (0 Sunday / 1 Monday, default Monday)
- ✕Single-date picker (use Calendar primitive)
- ✕Activity log (use Timeline)
Install
Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.
Usage
Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.
Props
eventsEvents to render. CalendarEvent = { id, start, end?, title, subtitle?, description?, tone?, accent?, calendarId?, recurring?, allDay? }. An allDay event is lifted into a LANE above the hour grid in the week and day views rather than being given an hour it does not have — hosts used to pin them to 00:00 to make them visible, which filled the top of the day and claimed a time the event has no business claiming. The lane renders only when the period has one, so a calendar with nothing all-day is unchanged. MCP feedback fb_1571eb42.
yesviewActive view. "agenda" is a list grouped by day over the SAME range the month grid paints, so switching between the two needs no refetch and onRangeChange stays quiet; days with nothing on them are skipped and an empty period is its own state. MCP feedback fb_1571eb42.
—referenceDateThe period to show. An ISO string is accepted, like CalendarEvent.start — a host whose dates came out of JSON has strings, and passing one used to throw during render. Alone: the initial date, re-synced when the prop changes while the toolbar still navigates itself. Together with onReferenceDateChange it is fully CONTROLLED — every navigation control reports and the calendar shows what the host renders
—defaultReferenceDateWhere an uncontrolled calendar starts. An ISO string is accepted. Use with onReferenceDateChange to be told about navigation while the calendar still moves itself. Ignored when referenceDate is set
—onReferenceDateChangeFired whenever the visible period moves (chevrons, Today, a mini-calendar day, "+n more"). Not on mount, and not for a view-only change
—onRangeChangeThe days now on screen — fired once on mount and once per real change, including a view switch. end is inclusive (23:59:59.999 of the last visible day); a month covers the whole 42-cell grid. Never fires twice for the same range, so an inline arrow does not turn renders into fetches
—onEventClickEvent click handler
—calendarsThe calendars events belong to, for the legend and per-calendar colour. CalendarSource = { id, name, tone?, accent?, visible? }. `accent` is this calendar’s OWN colour and takes any CSS colour value (“#7c5cff”, “oklch(0.7 0.2 160)”, “var(--color-chart-2)”): six tone tokens cannot tell eight calendars apart, and a CalDAV or iCal calendar arrives carrying its own hex. The value is written to a --calendar-accent custom property inline and read back by arbitrary utilities, so it does not depend on Tailwind having seen the colour — which is exactly what the deprecated `color` prop (raw Tailwind CLASSES, composed at runtime, invisible to the static scanner) got wrong. An event can carry its own `accent` too, and it wins over its calendar’s. MCP feedback fb_1571eb42.
—onCreateEventCreate handler — the toolbar's New event button and the cell + affordances render only when this is set (omit for derived/read-only calendars)
—labelsOverride any subset of the built-in UI strings (today, previous/next aria, day/week/month/agenda tabs, allDay and allDayEvents for the all-day lane, agendaEmptyTitle and agendaEmptyHint, newEvent, myCalendars, addEvent, moreEvents '+{count} more', showAllEvents). English defaults ship as DEFAULT_BUILD_CALENDAR_LABELS. Month/weekday names and header dates are NOT labels — wrap the app in LocaleProvider ({ locale, calendar: 'buddhist', weekStartsOn }) instead
—