GoBrand Calendar

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.

Month with SuspenseKeep navigation available while the visible month's events load.

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

On this page