Data and Suspense
Keep calendar controls available while a view loads its events.
Create the calendar without events above the Suspense boundary. Fetch and project the events inside the view.
import { Suspense } from 'react'
import type { Temporal } from '@js-temporal/polyfill'
import { useSuspenseQuery } from '@tanstack/react-query'
import {
CalendarProvider,
useCalendar,
useCalendarView,
useCreateCalendar,
} from '@gobrand/react-calendar'
type Event = {
id: string
title: string
date: Temporal.PlainDate
}
type CalendarProps = {
loadEvents(
start: Temporal.ZonedDateTime,
end: Temporal.ZonedDateTime,
): Promise<Event[]>
}
function Calendar({ loadEvents }: CalendarProps) {
const calendar = useCreateCalendar({ views: { month: true } })
return (
<CalendarProvider calendar={calendar}>
<Controls />
<Suspense fallback={<p>Loading events…</p>}>
<MonthView loadEvents={loadEvents} />
</Suspense>
</CalendarProvider>
)
}
function Controls() {
const calendar = useCalendar()
return (
<nav>
<button onClick={calendar.previous}>Previous</button>
<strong>{calendar.title()}</strong>
<button onClick={calendar.next}>Next</button>
</nav>
)
}
function MonthView({ loadEvents }: CalendarProps) {
const calendar = useCalendar()
const { start, end } = calendar.visibleRange
const { data: events } = useSuspenseQuery({
queryKey: ['events', start.toString(), end.toString()],
queryFn: () => loadEvents(start, end),
})
const month = useCalendarView({
view: 'month',
items: events,
getDate: event => event.date,
})
return month.weeks.map(week => (
<div key={week[0].id}>
{week.map(day => (
<div key={day.id}>
{day.date.toString()}: {day.items.map(event => event.title).join(', ')}
</div>
))}
</div>
))
}Controls does not unmount when MonthView suspends, so navigation remains available while events load.
The example assumes your app already has TanStack Query's QueryClientProvider.
Different data for each view
Each useCalendarView call infers its own item type. A month view can use summaries while a timed week view uses full event records. The shared controller does not need to know either type.
July 2026
Sun
Mon
Tue
Wed
Thu
Fri
Sat
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