Skip to main content

Lesson 3: Building a Grid

Using buildCalendarMonth to generate and render a calendar month grid.

The grid builder

buildCalendarMonth is a pure function. Pass an adapter, a date, and optional config; get back a Grid.ICalendarMonth with weeks and day cells.

import { NativeAdapter, buildCalendarMonth } from '@gentleduck/calendar'
 
const adapter = new NativeAdapter()
const march2026 = new Date(2026, 2, 1)
 
const month = buildCalendarMonth(adapter, march2026, {
  showOutsideDays: true,
  fixedWeeks: false,
})
 
// month.weeks is an array of Grid.ICalendarWeek objects
// Each week has 7 Grid.ICalendarDay objects
import { NativeAdapter, buildCalendarMonth } from '@gentleduck/calendar'
 
const adapter = new NativeAdapter()
const march2026 = new Date(2026, 2, 1)
 
const month = buildCalendarMonth(adapter, march2026, {
  showOutsideDays: true,
  fixedWeeks: false,
})
 
// month.weeks is an array of Grid.ICalendarWeek objects
// Each week has 7 Grid.ICalendarDay objects

What you get back

// Grid.ICalendarMonth<Date>
{
  month: Date,          // First day of the month
  weeks: [
    {
      weekNumber: 9,    // ISO week number
      days: [
        {
          date: Date,           // The date
          isToday: false,       // Is this today?
          isOutside: true,      // From adjacent month?
          isHidden: false,      // Hidden when showOutsideDays is false?
          isWeekend: true,      // Saturday or Sunday?
          isSelected: false,    // Set by applySelection()
          isDisabled: false,    // Set by applySelection()
          isRangeStart: false,  // Set by applySelection()
          isRangeEnd: false,    // Set by applySelection()
          isRangeMiddle: false, // Set by applySelection()
        },
        // ... 6 more days
      ]
    },
    // ... more weeks
  ]
}
// Grid.ICalendarMonth<Date>
{
  month: Date,          // First day of the month
  weeks: [
    {
      weekNumber: 9,    // ISO week number
      days: [
        {
          date: Date,           // The date
          isToday: false,       // Is this today?
          isOutside: true,      // From adjacent month?
          isHidden: false,      // Hidden when showOutsideDays is false?
          isWeekend: true,      // Saturday or Sunday?
          isSelected: false,    // Set by applySelection()
          isDisabled: false,    // Set by applySelection()
          isRangeStart: false,  // Set by applySelection()
          isRangeEnd: false,    // Set by applySelection()
          isRangeMiddle: false, // Set by applySelection()
        },
        // ... 6 more days
      ]
    },
    // ... more weeks
  ]
}

Rendering the grid

A minimal React component that renders the grid:

import { NativeAdapter, buildCalendarMonth } from '@gentleduck/calendar'
 
const adapter = new NativeAdapter()
 
function StaticCalendar({ date }: { date: Date }) {
  const month = buildCalendarMonth(adapter, date, {
    showOutsideDays: true,
  })
 
  return (
    <table>
      <thead>
        <tr>
          {['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(d => (
            <th key={d}>{d}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {month.weeks.map((week, i) => (
          <tr key={i}>
            {week.days.map(day => (
              <td
                key={day.date.getTime()}
                style={{
                  opacity: day.isOutside ? 0.4 : 1,
                  fontWeight: day.isToday ? 'bold' : 'normal',
                }}>
                {day.date.getDate()}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  )
}
import { NativeAdapter, buildCalendarMonth } from '@gentleduck/calendar'
 
const adapter = new NativeAdapter()
 
function StaticCalendar({ date }: { date: Date }) {
  const month = buildCalendarMonth(adapter, date, {
    showOutsideDays: true,
  })
 
  return (
    <table>
      <thead>
        <tr>
          {['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(d => (
            <th key={d}>{d}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {month.weeks.map((week, i) => (
          <tr key={i}>
            {week.days.map(day => (
              <td
                key={day.date.getTime()}
                style={{
                  opacity: day.isOutside ? 0.4 : 1,
                  fontWeight: day.isToday ? 'bold' : 'normal',
                }}>
                {day.date.getDate()}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  )
}

Multi-month grids

buildMultiMonth generates several consecutive months:

import { buildMultiMonth } from '@gentleduck/calendar'
 
const months = buildMultiMonth(adapter, new Date(), 3, {
  showOutsideDays: true,
})
 
// months[0] - current month
// months[1] - next month
// months[2] - month after next
import { buildMultiMonth } from '@gentleduck/calendar'
 
const months = buildMultiMonth(adapter, new Date(), 3, {
  showOutsideDays: true,
})
 
// months[0] - current month
// months[1] - next month
// months[2] - month after next

Grid config options

OptionTypeDefaultDescription
showOutsideDaysbooleantrueWhen false, outside days are hidden and disabled
fixedWeeksbooleanfalseAlways produces 6 weeks
localeCalendarLocaleConfig{}Locale settings (see below)

locale.weekStartDay sets the first day of the week (0 = Sunday, 1 = Monday). Pass it inside locale:

buildCalendarMonth(adapter, date, {
  locale: { weekStartDay: 1 }, // Monday
})
buildCalendarMonth(adapter, date, {
  locale: { weekStartDay: 1 }, // Monday
})