NEW Dialogh community is now live on WhatsApp! Join us โ†’
Svelte 5
SvelteKit
Calendar

Big Calendar (Svelte)

Created: 7/9/2026 Updated: 7/12/2026 Completed
Big Calendar (Svelte)

About

A feature-rich calendar for SvelteKit, built with Svelte 5 runes, Tailwind v4, and shadcn-svelte. Day, week, month, and agenda views with drag-and-drop events. A Svelte port of the popular lramos33/big-calendar.

Documentation

Big Calendar โ€” Svelte

A feature-rich calendar built with SvelteKit, Svelte 5 runes, TypeScript, and Tailwind CSS v4 โ€” five views, full event CRUD, drag-and-drop, per-user filtering, configurable working hours, and event locations with online-meeting links and an embedded map.

License: MITSvelte 5 SvelteKit 2 TypeScript Tailwind CSS v4

Month view

Big Calendar bundles five views, full event CRUD, drag-and-drop scheduling, undo/redo, a command palette, and optional per-event locations into one SvelteKit app. Everything runs in the browser โ€” events persist to localStorage, seeded from sample data on a first visit, with no backend to set up.

Features

  • ๐Ÿ“… Five calendar views
    • Agenda, Year, Month, Week (detailed time slots), Day (hourly breakdown)
  • โœ๏ธ Create, edit, and delete events
    • Drag across empty grid slots in the week or day view to block out a new event
    • Your events are saved in this browser (localStorage), and survive a reload
    • "Reset to sample data" in settings puts the demo back the way it started
  • โ†ฉ๏ธ Undo and redo
    • โŒ˜Z / Ctrl+Z undoes, โ‡งโŒ˜Z / Ctrl+Y redoes โ€” every create, edit, move, resize, and delete
    • Each change raises a toast with an Undo button
  • โŒจ๏ธ Command palette and keyboard shortcuts
    • โŒ˜K / Ctrl+K to switch views, jump to today, create an event, or search events by title
    • t today, n new event, g then d/w/m/y/a to switch view, โ†/โ†’ to page, ? for the full list
  • ๐Ÿ“ Event locations
    • Attach an optional location to any event: an online meeting or a physical address
    • Online links show the platform's favicon and a one-click Join button โ€” the platform (Zoom, Google Meet, Teams, Jitsi, โ€ฆ) is detected from the URL, so any link works
    • Physical addresses render on an embedded map, geocoded from the address string
    • See Event locations
  • ๐ŸŽจ Event customization
    • Seven event colors
    • Three badge variants (dot, colored, mixed)
    • Single- and multi-day events, with multi-day events spanning cells
    • Busy month cells reveal their hidden events in a popover
  • ๐Ÿ–ฑ๏ธ Drag and drop
    • Drag events onto another day in the month view, or onto any quarter-hour slot in the week and day views
    • Drag across empty slots to create an event over exactly that range; Esc abandons the drag
    • Resize events by their top or bottom edge to change start or end time, with keyboard support
  • ๐Ÿ‘ฅ User management
    • Filter events by user, or view everyone at once
    • Avatar group in the user picker
  • โšก Real-time
    • Live current-time indicator on the week and day grids
    • "Happening now" panel in the day view
  • โฐ Time customization
    • Per-weekday working hours, with non-working hours hatched
    • Adjustable visible-hours range that auto-expands to fit outlying events
  • ๐ŸŽฏ UI/UX
    • Responsive, accessible, keyboard-navigable
    • Light and dark themes, toggled from the header and remembered across reloads
    • Loading skeletons until events hydrate, and empty states that offer a Create button

Screenshots

The five views

Month โ€” the busy-cell overflow gridWeek โ€” quarter-hour time slots
Month viewWeek view
Day โ€” hourly breakdownYear โ€” twelve months at a glance
Day viewYear view

Agenda โ€” a chronological list grouped by day

Agenda view

Locations

An event can carry an optional location. Online links resolve the platform from the URL and offer a one-click Join; physical addresses render on an embedded Leaflet map, geocoded from the address string.

Online meeting โ€” favicon + JoinPhysical address โ€” embedded map
Online meeting detailsPhysical location with map

Creating, editing, and settings

Add event โ€” validated formEdit event โ€” location pre-expanded
Add event dialogEdit event dialog
Location picker โ€” online / in personCalendar settings โ€” badge style, visible & working hours
Location pickerCalendar settings

Command palette, shortcuts, and filtering

Command palette (โŒ˜K / Ctrl+K)Keyboard shortcuts (?)
Command paletteKeyboard shortcuts

Filter by user โ€” an avatar group in the picker, or pick one person

User select

Dark mode

Every view supports a light and a dark theme, toggled from the header and remembered across reloads.

Show dark-theme views
Month view (dark)Week view (dark)
Day view (dark)Year view (dark)

Agenda view (dark)

Tech stack

Choice
FrameworkSvelteKit 2 / Svelte 5 (runes)
LanguageTypeScript
StylingTailwind CSS v4
UI componentsshadcn-svelte (bits-ui)
Formssveltekit-superforms + formsnap + zod 4
Date handlingdate-fns v4
MapsLeaflet + OpenStreetMap
Icons@lucide/svelte
StateSvelte 5 runes + context
Package managerbun

Getting started

git clone https://github.com/DevRohit06/big-calendar-svelte.git
cd big-calendar-svelte
bun install
bun run dev

Open http://localhost:5173. The root route redirects to /month-view.

Other scripts:

bun run check     # svelte-check + tsc
bun run lint      # prettier --check && eslint
bun run format    # prettier --write
bun run build     # production build

Usage

1. Provide the calendar state

Wrap your page or layout in CalendarProvider. It creates the shared state on context and, in a client-only effect, seeds events from localStorage (or requests.ts on a first visit):

<!-- +layout.svelte -->
<script lang="ts">
	import CalendarProvider from '$lib/calendar/contexts/calendar-provider.svelte';
	let { children } = $props();
</script>

<CalendarProvider>
	{@render children()}
</CalendarProvider>

Wiring the calendar into an existing app or a real backend? See the integration guide.

2. Render a view

<script lang="ts">
	import ClientContainer from '$lib/calendar/components/client-container.svelte';
</script>

<ClientContainer view="month" />

view accepts "day" | "week" | "month" | "year" | "agenda".

3. Read and control state anywhere

The calendar exposes its state as a class instance from context:

<script lang="ts">
	import { getCalendarState } from '$lib/calendar/contexts/calendar-context.svelte';

	const calendar = getCalendarState();

	// Read properties off `calendar` directly โ€” destructuring loses reactivity.
	function jumpToToday() {
		calendar.selectDate(new Date());
	}
</script>

<p>Showing {calendar.selectedDate.toDateString()}</p>

CalendarState exposes the reactive fields selectedDate, selectedUserId, badgeVariant, visibleHours, workingHours, users, events, and hydrated, plus the methods selectDate(), addEvent(), updateEvent(), deleteEvent(), undo(), redo(), and reset(). See the integration guide to route these through a backend.

Data structures

interface IEvent {
	id: number;
	title: string;
	description: string;
	startDate: string; // ISO string
	endDate: string; // ISO string
	color: 'blue' | 'green' | 'red' | 'yellow' | 'purple' | 'orange' | 'gray';
	user: IUser;
	location?: IEventLocation; // optional โ€” see "Event locations" below
}

interface IUser {
	id: string;
	name: string;
	picturePath: string | null;
}

// A location is either an online meeting or a physical place โ€” never a mix of
// both, so it is modeled as a discriminated union.
type IEventLocation =
	| { type: 'online'; url: string }
	| { type: 'physical'; address: string; lat?: number; lng?: number };

Note: src/lib/calendar/mocks.ts generates its events with Math.random() at module load, so only import it from a server load. Pulling it into a component would generate different events on the server and the client, and hydration would tear.

Creating events

AddEventDialog validates with zod and, on submit, calls calendar.addEvent(), which appends the event, records an undo snapshot, and writes through to localStorage. To persist to a backend instead, swap the body of addEvent/updateEvent/deleteEvent on CalendarState โ€” every mutation already routes through them.

Project structure

src/
โ”œโ”€โ”€ routes/
โ”‚   โ”œโ”€โ”€ +layout.svelte                # ModeWatcher + CalendarProvider
โ”‚   โ”œโ”€โ”€ layout.css                    # Tailwind v4 theme, custom utilities
โ”‚   โ””โ”€โ”€ {day,week,month,year,agenda}-view/
โ”œโ”€โ”€ lib/
โ”‚   โ”œโ”€โ”€ calendar/
โ”‚   โ”‚   โ”œโ”€โ”€ components/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ agenda-view/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dialogs/              # Add / edit / details dialogs
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dnd/                  # Drag, resize, drag-to-create
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ header/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ month-view/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ week-and-day-view/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ year-view/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ command-palette.svelte
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ keyboard-shortcuts.svelte
โ”‚   โ”‚   โ”œโ”€โ”€ contexts/                 # CalendarState class ($state runes)
โ”‚   โ”‚   โ”œโ”€โ”€ helpers.ts                # Pure date/layout math
โ”‚   โ”‚   โ”œโ”€โ”€ history.ts                # Snapshot undo/redo stacks
โ”‚   โ”‚   โ”œโ”€โ”€ interfaces.ts
โ”‚   โ”‚   โ”œโ”€โ”€ notifications.ts          # Undoable toasts
โ”‚   โ”‚   โ”œโ”€โ”€ schemas.ts                # zod event schema
โ”‚   โ”‚   โ”œโ”€โ”€ storage.ts                # Versioned localStorage codec
โ”‚   โ”‚   โ””โ”€โ”€ types.ts
โ”‚   โ”œโ”€โ”€ components/ui/                # shadcn-svelte + two custom components
โ”‚   โ””โ”€โ”€ hooks/

Event locations

Where an event happens matters as much as when โ€” a Zoom link and a street address are not the same thing. Every event can carry an optional location, modeled as a discriminated union so an online meeting and a physical place never blur together:

  • Online โ€” a meeting link. The platform name and favicon are derived from the URL's host, so Zoom, Google Meet, Teams, Jitsi, Webex, Whereby, or a self-hosted link all work without a hard-coded provider list. The details dialog shows a one-click Join button.
  • Physical โ€” a free-text address (any country). The details dialog renders it on an embedded Leaflet map, geocoded on the fly from the address.

Design notes:

  • Optional and non-breaking. Events without a location behave exactly as before; an empty location is stripped on submit, so nothing is stored until you actually fill it in.
  • The map never touches SSR. Leaflet is loaded from a CDN inside onMount, so it never enters the server module graph โ€” the same client-only pattern the calendar shell already uses for its shimmer library. No new npm dependency is added, and the server bundle contains zero map code.
  • Favicons come from a public icon service keyed on the link's host, which falls back to a generic globe for unknown hosts, so the icon never renders broken.

Locations are geocoded on display via OpenStreetMap's Nominatim (public endpoint, ~1 req/sec). A production deployment would want a dedicated geocoding key and to persist the resulting lat/lng โ€” the IEventLocation type already carries the optional fields for it.

Roadmap

  • Persist badge variant and working hours across reloads.
  • Persist geocoded lat/lng and swap Nominatim for a keyed geocoding service.
  • Component tests (vitest + vitest-browser-svelte are already configured).

Contributing

Contributions are welcome โ€” issues and pull requests both. Please run bun run check and bun run lint before submitting.

Credits

A Svelte reimagining of an idea by Leonardo Ramos โ€” MIT.

Built with SvelteKit, shadcn-svelte, bits-ui, date-fns, and Tailwind CSS.

License

MIT. ยฉ 2026 Rohit Kushwaha, with ยฉ 2025 Leonardo Ramos for the earlier work this builds on โ€” both notices are retained in the LICENSE file, as MIT requires.