fullcalendar-svelte-shadcn Svelte Themes

Fullcalendar Svelte Shadcn

Svelte wrapper or FullCalendar, using ShadCN components for interactive elements. Themeable and documented.

FullCalendar Svelte + shadcn

A Svelte 5 component library wrapping FullCalendar with shadcn-svelte styling. Fully themeable, accessible, and designed to integrate seamlessly with your existing shadcn design system.

Live Demo

Features

  • Svelte 5 Runes - Built with $state, $derived, and $effect for optimal reactivity
  • Full FullCalendar Power - Day, week, month views with drag-and-drop, resizing, and selection
  • Themeable - Automatically inherits your shadcn theme via CSS custom properties
  • Dark Mode Ready - Works with your existing dark mode setup out of the box
  • Tree-shakeable - Only import what you need
  • TypeScript - Full type definitions included

Installation

npm install @agsci/fullcalendar-svelte-shadcn @fullcalendar/core

Optional Plugins

Install the FullCalendar plugins you need:

# For day/week views (time grid)
npm install @fullcalendar/daygrid @fullcalendar/timegrid

# For drag-and-drop and selection
npm install @fullcalendar/interaction

# For list view
npm install @fullcalendar/list

Quick Start

<script lang="ts">
  import { Calendar, CalendarNav, type CalendarEvent, type CalendarView } from '@agsci/fullcalendar-svelte-shadcn';
  import '@agsci/fullcalendar-svelte-shadcn/styles';

  let events = $state<CalendarEvent[]>([
    {
      id: '1',
      title: 'Team Meeting',
      start: new Date(),
      end: new Date(Date.now() + 60 * 60 * 1000),
      backgroundColor: '#3b82f6'
    }
  ]);

  let viewedDate = $state(new Date());
  let currentView = $state<CalendarView>('week');
  let calendarRef: { prev: () => void; next: () => void; today: () => void; changeView: (v: CalendarView) => void };
</script>

<CalendarNav
  {viewedDate}
  {currentView}
  onPrev={() => calendarRef?.prev()}
  onNext={() => calendarRef?.next()}
  onToday={() => calendarRef?.today()}
  onViewChange={(v) => { currentView = v; calendarRef?.changeView(v); }}
/>

<Calendar
  bind:this={calendarRef}
  {events}
  config={{ initialView: 'week', editable: true, selectable: true }}
  onEventClick={(event) => console.log('Clicked:', event)}
  onDatesSet={(start) => viewedDate = start}
  onViewChange={(v) => currentView = v}
/>

Components

Calendar

The main calendar component wrapping FullCalendar.

<Calendar
  events={events}
  config={{
    initialView: 'week',      // 'day' | 'week' | 'month' | 'list'
    slotMinTime: '07:00',     // Earliest time slot
    slotMaxTime: '21:00',     // Latest time slot
    height: '600px',          // Calendar height
    editable: true,           // Enable drag-and-drop
    selectable: true,         // Enable date range selection
    nowIndicator: true,       // Show current time line
    firstDay: 0               // Week start (0=Sunday, 1=Monday)
  }}
  onEventClick={(event) => {}}
  onEventChange={(event, oldEvent, revert) => {}}
  onDateSelect={(start, end, allDay) => {}}
  onDateClick={(date, allDay) => {}}
  onDatesSet={(start, end) => {}}
  onViewChange={(view) => {}}
/>

CalendarNav

Navigation controls with month/year selectors and view switcher.

<CalendarNav
  viewedDate={viewedDate}
  currentView={currentView}
  onPrev={() => calendarRef?.prev()}
  onNext={() => calendarRef?.next()}
  onToday={() => calendarRef?.today()}
  onViewChange={(view) => calendarRef?.changeView(view)}
  onDateChange={(date) => calendarRef?.gotoDate(date)}
/>

Event Dialogs

Pre-built dialogs for viewing, adding, editing, and deleting events.

<script>
  import { EventView, EventAddDialog, EventEditDialog, EventDeleteDialog } from '@agsci/fullcalendar-svelte-shadcn';
</script>

<EventView
  event={selectedEvent}
  open={viewOpen}
  onClose={() => viewOpen = false}
  onEdit={(event) => { /* open edit dialog */ }}
  onDelete={(event) => { /* open delete dialog */ }}
/>

<EventAddDialog
  open={addOpen}
  defaultStart={selectedStart}
  defaultEnd={selectedEnd}
  onClose={() => addOpen = false}
  onAdd={(eventData) => events = [...events, { id: Date.now().toString(), ...eventData }]}
/>

<EventEditDialog
  event={selectedEvent}
  open={editOpen}
  onClose={() => editOpen = false}
  onSave={(id, updates) => events = events.map(e => e.id === id ? { ...e, ...updates } : e)}
/>

<EventDeleteDialog
  event={selectedEvent}
  open={deleteOpen}
  onClose={() => deleteOpen = false}
  onConfirm={(id) => events = events.filter(e => e.id !== id)}
/>

Customization

Theming via CSS Custom Properties

The calendar automatically inherits your shadcn theme. It uses CSS custom properties that map to your theme tokens:

/* The library uses these variables (with sensible fallbacks) */
:root {
  --fc-border-color: var(--border);
  --fc-bg: var(--background);
  --fc-text: var(--foreground);
  --fc-muted: var(--muted-foreground);
  --fc-today-bg: var(--accent);
  --fc-hover-bg: var(--secondary);
  --fc-primary: var(--primary);
  --fc-primary-foreground: var(--primary-foreground);
}

No extra configuration needed - if you have shadcn-svelte set up, the calendar matches your theme automatically.

Custom Theme Overrides

Override specific calendar styles in your app's CSS:

/* Make the calendar more compact */
:root {
  --fc-slot-height: 2.5rem;
  --fc-header-height: 2.5rem;
}

/* Custom event colors */
.fc-event {
  --fc-event-radius: 0.5rem;
}

/* Different today highlight */
.fc-day-today {
  --fc-today-bg: hsl(var(--primary) / 0.1);
}

Dark Mode

The calendar respects your dark mode setup. If you use the standard .dark class approach:

.dark {
  --border: hsl(240 3.7% 15.9%);
  --background: hsl(240 10% 3.9%);
  --foreground: hsl(0 0% 98%);
  /* ... your other dark mode tokens */
}

The calendar will automatically adapt.

Component-Level Styling

Pass custom classes to components:

<Calendar class="rounded-lg shadow-lg" />
<CalendarNav class="mb-4 px-2" />

Event Styling

Control event appearance via the event data:

const event: CalendarEvent = {
  id: '1',
  title: 'Important Meeting',
  start: new Date(),
  end: new Date(),
  backgroundColor: '#ef4444',  // Red background
  textColor: '#ffffff',        // White text
};

Or use the color picker in the Add/Edit dialogs.

Types

interface CalendarEvent {
  id: string;
  title: string;
  start: Date;
  end: Date;
  description?: string;
  backgroundColor?: string;
  textColor?: string;
  allDay?: boolean;
  extendedProps?: Record<string, unknown>;
}

interface CalendarConfig {
  initialView?: 'day' | 'week' | 'month' | 'list';
  slotMinTime?: string;  // e.g., '07:00'
  slotMaxTime?: string;  // e.g., '21:00'
  firstDay?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
  editable?: boolean;
  selectable?: boolean;
  nowIndicator?: boolean;
  height?: string | number;
  allDaySlot?: boolean;
  timeZone?: string;
}

type CalendarView = 'day' | 'week' | 'month' | 'list';

State Management

For complex apps, use the provided state utilities:

import { createCalendarState, setCalendarContext, getCalendarContext } from '@agsci/fullcalendar-svelte-shadcn';

// In a parent component
const calendarState = createCalendarState(initialEvents);
setCalendarContext(calendarState);

// In child components
const state = getCalendarContext();
state.addEvent(newEvent);
state.selectEvent(event);
state.openModal('add');

Utilities

Helpful date utilities are exported:

import {
  formatDate,      // 'Dec 8, 2024'
  formatTime,      // '2:30 PM'
  formatDateRange, // 'Dec 8, 2:00 PM - 3:00 PM'
  isSameDay,
  isToday,
  toDateString,    // '2024-12-08' (for inputs)
  toTimeString,    // '14:30' (for inputs)
} from '@agsci/fullcalendar-svelte-shadcn';

Requirements

  • Svelte 5.x
  • @fullcalendar/core 6.x
  • A project with shadcn-svelte set up (for theme tokens)

Development

# Install dependencies
pnpm install

# Start dev server
pnpm dev

# Type check
pnpm check

# Build library
pnpm build

License

MIT

Credits

Top categories

Loading Svelte Themes