kalendly Svelte Themes

Kalendly

A universal calendar scheduler component that works seamlessly across React, Vue, and React Native with full TypeScript support.

kalendly

A universal calendar web component — works in React, Vue, Svelte, Angular, Solid.js, and plain HTML with no framework dependency.

Features

  • Framework-agnostic: Single <kal-calendar> custom element, no framework required
  • Responsive: Mobile-friendly, matches your existing UI
  • Themeable: CSS variables + JS property API
  • Type Safe: Full TypeScript support
  • Event-rich: Categories, priorities, time ranges, attendees, and more
  • Accessible: Built with accessibility in mind
  • Tree-shakeable: Import only what you need

Live Demo

Try the Interactive Demo →

Vanilla JS Demo React Demo Vue Demo Angular Demo Svelte Demo Solid.js Demo

Installation

npm install kalendly

Usage

Vanilla HTML / CDN

<link
  rel="stylesheet"
  href="https://unpkg.com/kalendly/dist/styles/calendar.css"
/>
<script src="https://unpkg.com/kalendly/dist/index.umd.js"></script>

<kal-calendar
  id="cal"
  title="My Calendar"
  initial-date="2025-01-15"
></kal-calendar>

<script>
  const cal = document.getElementById('cal');

  // Set events (JS property — not an attribute)
  cal.events = [
    { id: 1, name: 'Team Meeting', date: new Date(2025, 0, 15) },
    { id: 2, name: 'Project Deadline', date: new Date(2025, 0, 20) },
  ];

  // Listen to custom events
  cal.addEventListener('cal-date-select', e => {
    console.log('Selected:', e.detail.date, e.detail.events);
  });

  cal.addEventListener('cal-month-change', e => {
    console.log('Month:', e.detail.year, e.detail.month);
  });
</script>

ES Modules

import 'kalendly';
import 'kalendly/styles';

// <kal-calendar> is now registered and ready

React 19

React 19 has full custom element support — pass objects/arrays as props and listen to custom events directly.

import 'kalendly';
import 'kalendly/styles';

function App() {
  return (
    <kal-calendar
      title="My Calendar"
      events={events}
      oncal-date-select={e => console.log(e.detail.date)}
      oncal-month-change={e => console.log(e.detail.year, e.detail.month)}
    />
  );
}

React 18 users: React 18 does not forward object/array props or custom events to custom elements. You need to wire these up via a ref:

import { useRef, useEffect } from 'react';
import 'kalendly';

function Calendar({ events, onDateSelect, onMonthChange, ...attrs }) {
  const ref = useRef(null);

  useEffect(() => {
    if (ref.current) ref.current.events = events;
  }, [events]);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const onSelect = e => onDateSelect?.(e.detail.date, e.detail.events);
    const onChange = e => onMonthChange?.(e.detail.year, e.detail.month);
    el.addEventListener('cal-date-select', onSelect);
    el.addEventListener('cal-month-change', onChange);
    return () => {
      el.removeEventListener('cal-date-select', onSelect);
      el.removeEventListener('cal-month-change', onChange);
    };
  }, [onDateSelect, onMonthChange]);

  return <kal-calendar ref={ref} {...attrs} />;
}

Vue 3

Vue 3 supports custom elements natively — bind props with : and listen to events with @:

<template>
  <kal-calendar
    title="My Calendar"
    :events="events"
    @cal-date-select="onDateSelect"
    @cal-month-change="onMonthChange"
  />
</template>

<script setup>
import 'kalendly';
import 'kalendly/styles';

const events = [{ id: 1, name: 'Team Meeting', date: new Date(2025, 0, 15) }];

function onDateSelect(e) {
  console.log('Selected:', e.detail.date);
}

function onMonthChange(e) {
  console.log('Month:', e.detail.year, e.detail.month);
}
</script>

Angular

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

// app.component.ts
import 'kalendly';
import 'kalendly/styles';
<!-- app.component.html -->
<kal-calendar
  title="My Calendar"
  [events]="events"
  (cal-date-select)="onDateSelect($event)"
  (cal-month-change)="onMonthChange($event)"
></kal-calendar>

Svelte 5

<script>
  import 'kalendly';
  import 'kalendly/styles';
  let { events = [] } = $props();
</script>

<kal-calendar {events} oncal-date-select={e => console.log(e.detail.date)} />

Svelte 4

<script>
  import { onMount } from 'svelte';
  import 'kalendly';
  import 'kalendly/styles';
  export let events = [];
  let calEl;
  onMount(() => { calEl.events = events; });
  $: if (calEl) calEl.events = events;
</script>

<kal-calendar bind:this={calEl} on:cal-date-select on:cal-month-change />

Solid.js

import 'kalendly';
import 'kalendly/styles';

function App() {
  return (
    <kal-calendar
      title="My Calendar"
      prop:events={events}
      on:cal-date-select={e => console.log(e.detail.date)}
    />
  );
}

Migration from v0.1.x

v0.2.0 replaces the four separate framework packages with a single web component.

Before (v0.1.x) After (v0.2.0+)
import { Calendar } from 'kalendly/react' import 'kalendly'
import { Calendar } from 'kalendly/vue' import 'kalendly'
import { Calendar } from 'kalendly/react-native' Not supported
import { createCalendar } from 'kalendly/vanilla' import 'kalendly'
<Calendar events={events} /> <kal-calendar events={events} />

React Native is out of scope and not replaced.

Styling

Loading styles

// Bundler (Vite, webpack) — add once in your app entry (e.g. main.tsx)
import 'kalendly/styles';

// Plain HTML
// <link rel="stylesheet" href="/node_modules/kalendly/dist/styles/calendar.css">

// Angular — add to angular.json → projects → architect → build → styles
// "node_modules/kalendly/dist/styles/calendar.css"

Overriding styles

kalendly uses Light DOM — all standard CSS techniques work:

/* 1. CSS custom properties (recommended) */
:root {
  --calendar-primary-color: #6366f1;
  --calendar-background: #1e1e2e;
  --calendar-border-color: #334155;
}

/* 2. Direct class overrides */
.kalendly-calendar .calendar--card {
  border-radius: 12px;
}
// 3. JS theme property
document.querySelector('kal-calendar').theme = {
  primary: '#6366f1',
  background: '#1e1e2e',
};

Attributes

Primitives are set as HTML attributes:

Attribute Type Default Description
title string Calendar title
initial-date string today ISO date string for initial view
min-year string currentYear - 30 Minimum year in picker
max-year string currentYear + 10 Maximum year in picker
week-starts-on "0"|"1" "0" Week start: 0 = Sunday, 1 = Monday
use-short-month-names string Present = use abbreviated month names

Properties

Rich objects are set as JS properties (not attributes):

Property Type Description
events CalendarEvent[] Events to display
theme CalendarTheme Custom theme colors
categoryColors CategoryColorMap Per-category color overrides
renderEvent (event: CalendarEvent) => string Custom event HTML renderer
renderNoEvents () => string Custom empty-state HTML renderer

Custom Events

Event detail shape Description
cal-date-select { date: Date, events: CalendarEvent[] } User clicked a date
cal-month-change { year: number, month: number } Month navigation occurred

Both events bubble and are composed (cross Shadow DOM boundaries).

JavaScript API

const cal = document.querySelector('kal-calendar');

cal.updateEvents(newEvents); // Re-render with new events
cal.updateTheme(newTheme); // Apply new theme
cal.goToDate(new Date(2025, 5, 1)); // Navigate to date
cal.getCurrentDate(); // Returns currently selected Date
cal.getEngine(); // Access CalendarEngine directly

CalendarEvent Interface

interface CalendarEvent {
  id: string | number;
  name: string;
  date: string | Date;

  startTime?: string; // e.g. "09:00"
  endTime?: string; // e.g. "10:00"
  allDay?: boolean;

  description?: string;
  color?: string;
  category?:
    | 'work'
    | 'personal'
    | 'meeting'
    | 'deadline'
    | 'appointment'
    | 'other';
  location?: string;
  url?: string;

  status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
  priority?: 'low' | 'medium' | 'high';

  attendees?: string[];
  organizer?: string;
  reminders?: number[]; // minutes before event
  recurring?: {
    frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
    interval?: number;
    endDate?: string | Date;
    daysOfWeek?: number[];
  };

  notes?: string;
  tags?: string[];
  [key: string]: unknown;
}

Theming

All CSS variables

:root {
  --calendar-primary-color: #fc8917;
  --calendar-secondary-color: #fca045;
  --calendar-tertiary-color: #fdb873;
  --calendar-text-color: #2c3e50;
  --calendar-text-light: #6b7280;
  --calendar-border-color: #dee2e6;
  --calendar-today-outline: #f7db04;
  --calendar-event-indicator: #1890ff;
  --calendar-background: #fff;
  --calendar-cell-hover: #f3f4f6;
  --calendar-selected-bg: #eff6ff;
}

JS theme property (full reference)

cal.theme = {
  primary: '#3b82f6',
  secondary: '#60a5fa',
  tertiary: '#93c5fd',
  textColor: '#111827',
  textLight: '#6b7280',
  background: '#ffffff',
  cellHover: '#f3f4f6',
  borderColor: '#e5e7eb',
  todayOutline: '#fbbf24',
  selectedBg: '#eff6ff',
  eventIndicator: '#10b981',
};

Dark theme example

cal.theme = {
  primary: '#6366f1',
  secondary: '#818cf8',
  textColor: '#f9fafb',
  textLight: '#d1d5db',
  background: '#1f2937',
  cellHover: '#374151',
  borderColor: '#4b5563',
  todayOutline: '#fbbf24',
  selectedBg: '#312e81',
  eventIndicator: '#34d399',
};

Core Engine (advanced)

import { CalendarEngine } from 'kalendly/core';

const engine = new CalendarEngine({ events, initialDate: new Date() });

const unsubscribe = engine.subscribe(() => {
  const viewModel = engine.getViewModel();
  // re-render
});

engine.getActions().next();
engine.getActions().previous();
engine.getActions().jump(2025, 5);
engine.getActions().goToToday();

unsubscribe();
engine.destroy();

Browser Support

Custom Elements v1 — Chrome 67+, Firefox 63+, Safari 12.1+, Edge 79+.

Contributing

See CONTRIBUTING.md.

git clone https://github.com/callezenwaka/kalendly.git
cd kalendly
npm install
npm test
npm run dev:examples

License

MIT © Callis Ezenwaka

Changelog

See CHANGELOG.md.

Recent Updates

  • v0.2.0: Migrated to a single <kal-calendar> web component — works natively in React, Vue, Angular, and plain HTML with no framework dependency
  • v0.1.7: Vanilla calendar performance optimization with event delegation
  • v0.1.6: Navigation enhancements — Today button, month/year picker, optional title prop
  • v0.1.5: Universal theming system, TypeScript improvements
  • v0.1.0: Initial release with React, Vue, React Native, and Vanilla JavaScript support

Top categories

Loading Svelte Themes