react-big-calendar-nextjs-integration
Installation
SKILL.md
React Big Calendar + Next.js 16 + shadcn/ui Integration
Problem
react-big-calendar is a popular calendar library but requires careful integration with Next.js 16's App Router, especially regarding client/server component boundaries, CSS imports, and theming consistency with shadcn/ui.
Context / Trigger Conditions
- Building a calendar feature in Next.js 16 with App Router
- Using shadcn/ui for consistent design system
- Need server-side data fetching with client-side calendar interaction
- Want calendar events to match shadcn/ui color scheme
Solution
1. Install Dependencies
pnpm add react-big-calendar date-fns
pnpm add -D @types/react-big-calendar
Note: react-big-calendar works well with date-fns v4+ for date manipulation.
2. Create Client Component with Proper Imports
"use client";
import { useState, useMemo, useCallback } from "react";
import { Calendar, dateFnsLocalizer, View } from "react-big-calendar";
import { format, parse, startOfWeek, getDay } from "date-fns";
import { enUS } from "date-fns/locale";
// IMPORTANT: Import CSS in the client component
import "react-big-calendar/lib/css/react-big-calendar.css";
import "./custom-calendar.css"; // Your overrides
// Configure localizer OUTSIDE the component
const locales = { "en-US": enUS };
const localizer = dateFnsLocalizer({
format,
parse,
startOfWeek,
getDay,
locales,
});
export function MyCalendar({ events, onEventClick }) {
const [view, setView] = useState<View>("month");
const [date, setDate] = useState(new Date());
return (
<Calendar
localizer={localizer}
events={events}
view={view}
date={date}
onView={setView}
onNavigate={setDate}
onSelectEvent={onEventClick}
style={{ height: 600 }}
/>
);
}
3. Style Integration with shadcn/ui
Create custom-calendar.css to override react-big-calendar styles with shadcn/ui CSS variables:
/* Use shadcn/ui CSS variables for theming */
.rbc-calendar {
font-family: inherit;
}
.rbc-header {
padding: 0.75rem 0.5rem;
font-weight: 600;
border-bottom: 1px solid hsl(var(--border));
}
.rbc-today {
background-color: hsl(var(--accent));
}
.rbc-off-range-bg {
background-color: hsl(var(--muted));
}
.rbc-event {
border-radius: 0.25rem;
/* Use your event type colors */
}
.rbc-time-slot {
border-top: 1px solid hsl(var(--border));
}
4. Server Component Integration Pattern
// app/calendar/page.tsx (Server Component)
import { MyCalendarWrapper } from "./MyCalendarWrapper";
export default async function CalendarPage() {
// Fetch data server-side
const events = await db.event.findMany();
// Pass to client component
return <MyCalendarWrapper events={events} />;
}
// app/calendar/MyCalendarWrapper.tsx (Client Component)
"use client";
import { MyCalendar } from "@/components/calendar/MyCalendar";
import { useRouter } from "next/navigation";
export function MyCalendarWrapper({ events }) {
const router = useRouter();
const handleEventClick = (event) => {
// Client-side navigation
router.push(`/event/${event.id}`);
};
return <MyCalendar events={events} onEventClick={handleEventClick} />;
}
5. Event Styling with Custom Getter
const eventStyleGetter = useCallback((event) => {
let backgroundColor = "#3b82f6"; // default
switch (event.type) {
case "available":
backgroundColor = "hsl(var(--success))"; // green
break;
case "blocked":
backgroundColor = "hsl(var(--destructive))"; // red
break;
case "booked":
backgroundColor = "hsl(var(--primary))"; // blue
break;
}
return {
style: {
backgroundColor,
borderRadius: "4px",
opacity: 0.9,
color: "white",
border: "none",
},
};
}, []);
// In Calendar component
<Calendar
{...props}
eventPropGetter={eventStyleGetter}
/>
Verification
- Calendar renders without hydration errors
- CSS loads properly (no flash of unstyled content)
- Events display with consistent shadcn/ui colors
- Navigation and view switching work smoothly
- No "use client" directive errors
Example: Complete Calendar Component
"use client";
import { useState, useCallback } from "react";
import { Calendar, dateFnsLocalizer, View } from "react-big-calendar";
import { format, parse, startOfWeek, getDay } from "date-fns";
import { enUS } from "date-fns/locale";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import "react-big-calendar/lib/css/react-big-calendar.css";
import "./calendar-styles.css";
const locales = { "en-US": enUS };
const localizer = dateFnsLocalizer({
format, parse, startOfWeek, getDay, locales,
});
interface CalendarEvent {
id: string;
title: string;
start: Date;
end: Date;
type: "available" | "blocked" | "booked";
}
export function AppCalendar({
events,
onEventClick,
onDateSelect,
}: {
events: CalendarEvent[];
onEventClick?: (event: CalendarEvent) => void;
onDateSelect?: (date: Date) => void;
}) {
const [view, setView] = useState<View>("month");
const [date, setDate] = useState(new Date());
const eventStyleGetter = useCallback((event: CalendarEvent) => {
const colors = {
available: "#10b981",
blocked: "#ef4444",
booked: "#3b82f6",
};
return {
style: {
backgroundColor: colors[event.type],
borderRadius: "4px",
opacity: 0.9,
color: "white",
border: "none",
},
};
}, []);
return (
<Card className="p-4">
<Calendar
localizer={localizer}
events={events}
view={view}
date={date}
onView={setView}
onNavigate={setDate}
onSelectEvent={onEventClick}
onSelectSlot={({ start }) => onDateSelect?.(start)}
eventPropGetter={eventStyleGetter}
selectable
style={{ height: 600 }}
/>
</Card>
);
}
Notes
- Performance: For large event lists (>1000), consider virtualization or limiting date range
- Mobile: react-big-calendar is not mobile-optimized by default - use day view for mobile screens
- Timezone: Pass events with consistent timezone (UTC recommended), convert for display
- Custom Toolbar: Set
toolbar={false}and build custom navigation with shadcn/ui components - Accessibility: react-big-calendar has decent keyboard navigation built-in
- Types: Event interface should match your database schema
Common Pitfalls
- CSS Import Location: Must import CSS in client component, not server component or layout
- Localizer Creation: Create localizer outside component to avoid recreation on every render
- Date Objects: Ensure event start/end are JavaScript Date objects, not strings
- View State: Manage view and date state to enable custom navigation controls
- Event IDs: Always include unique
idfield on events for React keys
References
Weekly Installs
1
Repository
hankanman/claude-configFirst Seen
Mar 4, 2026
Security Audits
Installed on
windsurf1
amp1
cline1
openclaw1
trae1
qoder1