React Event Calendar

Installation

A simple, one-line command to get you started with React Event Calendar.

1. Installation

Run the following command in your terminal. This will install all required dependencies and copy the component files into your project.

npx npx shadcn@2.4.0-canary.12 add "https://shadcn-event-calendar.vercel.app/r/event-calendar.json"

2. Setup

Next, wrap your root layout with the NuqsAdapter for state management and add the Toaster for notifications.

app/layout.tsx
1import { ThemeProvider } from '@/components/theme-provider'; 2import { Toaster } from 'sonner'; 3import { NuqsAdapter } from '@/components/event-calendar/nuqs-adapter'; 4 5export default function RootLayout({ 6 children, 7}: { 8 children: React.ReactNode; 9}) { 10 return ( 11 <html lang="en" suppressHydrationWarning> 12 <body> 13 <ThemeProvider 14 attribute="class" 15 defaultTheme="system" 16 enableSystem 17 disableTransitionOnChange 18 > 19 <Toaster expand={true} richColors position="top-center" /> 20 <NuqsAdapter>{children}</NuqsAdapter> 21 </ThemeProvider> 22 </body> 23 </html> 24 ); 25}

3. Usage

You're all set! You can now use the EventCalendar component in your application. Here's an example of how to use it on a page with some dummy data.

app/page.tsx
1import { EventCalendar } from "@/components/event-calendar/event-calendar"; 2import { SearchParams } from "nuqs"; 3import { searchParamsCache } from "@/lib/searchParams"; 4import { Suspense } from "react"; 5import { Events } from "@/types/event"; 6 7const dummyEvents: Events[] = [ 8 { 9 id: "1", 10 title: "Daily Team Standup", 11 description: "Daily sync to discuss progress and blockers.", 12 startDate: new Date(), 13 endDate: new Date(), 14 startTime: "09:00", 15 endTime: "09:30", 16 isRepeating: true, 17 repeatingType: "daily", 18 location: "Virtual - Google Meet", 19 category: "Work", 20 color: "red", 21 createdAt: new Date(), 22 updatedAt: new Date(), 23 }, 24 { 25 id: "2", 26 title: "Project Alpha Deadline", 27 description: "Final submission for Project Alpha.", 28 startDate: new Date(new Date().setDate(new Date().getDate() + 2)), 29 endDate: new Date(new Date().setDate(new Date().getDate() + 2)), 30 startTime: "17:00", 31 endTime: "17:30", 32 isRepeating: false, 33 repeatingType: null, 34 location: "Project Management Platform", 35 category: "Project", 36 color: "blue", 37 createdAt: new Date(), 38 updatedAt: new Date(), 39 }, 40 { 41 id: "3", 42 title: "Weekly Review", 43 description: "Review of the past week and planning for the next.", 44 startDate: new Date( 45 new Date().setDate(new Date().getDate() - new Date().getDay() + 5) 46 ), 47 endDate: new Date( 48 new Date().setDate(new Date().getDate() - new Date().getDay() + 5) 49 ), 50 startTime: "15:00", 51 endTime: "16:00", 52 isRepeating: true, 53 repeatingType: "weekly", 54 location: "Conference Room B", 55 category: "Work", 56 color: "yellow", 57 createdAt: new Date(), 58 updatedAt: new Date(), 59 }, 60 { 61 id: "4", 62 title: "Dentist Appointment", 63 description: "Annual check-up.", 64 startDate: new Date(new Date().setDate(new Date().getDate() + 10)), 65 endDate: new Date(new Date().setDate(new Date().getDate() + 10)), 66 startTime: "11:00", 67 endTime: "12:00", 68 isRepeating: false, 69 repeatingType: null, 70 location: "City Dental Clinic", 71 category: "Personal", 72 color: "purple", 73 createdAt: new Date(), 74 updatedAt: new Date(), 75 }, 76]; 77 78interface DemoPageProps { 79 searchParams: Promise<SearchParams>; 80} 81 82export default async function Page(props: DemoPageProps) { 83 const searchParams = await props.searchParams; 84 const search = searchParamsCache.parse(searchParams); 85 86 const eventsResponse = { 87 events: dummyEvents, 88 }; 89 90 return ( 91 <div className="flex min-h-screen flex-col"> 92 <main className="flex-1 py-6"> 93 <div className="container"> 94 <div className="bg-card overflow-hidden rounded-xl border shadow-sm"> 95 <Suspense 96 fallback={ 97 <div className="flex h-[700px] items-center justify-center"> 98 <div className="flex flex-col items-center gap-2"> 99 <div className="border-primary h-8 w-8 animate-spin rounded-full border-4 border-t-transparent"></div> 100 <p className="text-muted-foreground text-sm"> 101 Loading calendar... 102 </p> 103 </div> 104 </div> 105 } 106 > 107 <EventCalendar 108 events={eventsResponse.events} 109 initialDate={search.date} 110 /> 111 </Suspense> 112 </div> 113 </div> 114 </main> 115 </div> 116 ); 117}