CalSync Documentation
Everything you need — from embedding the React calendar component to deploying your own self-hosted server.
Installation
Install CalSync into your React project. A valid license key is required to get the installation token for the private npm registry.
Step 1: Get Your Install Token
Use the license key from your email to get the npm install token. This token is needed to authenticate with the GitHub Packages registry.
Get Install TokenStep 2: Configure .npmrc
Create a .npmrc file in your project root with the token you received:
@calsync-react:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_INSTALL_TOKENStep 3: Install the package
npm install @calsync-react/calendarOr with other package managers:
pnpm add @calsync-react/calendaryarn add @calsync-react/calendarStep 4: Import styles
Import the CalSync stylesheet in your app entry point or layout:
import "@calsync-react/calendar/styles.css";Important: Add .npmrc to your .gitignore to avoid committing your token to version control.
Quick Start
The simplest way to use CalSync — drop it in and it works out of the box. In uncontrolled mode, CalSync manages events and agendas internally using localStorage.
import { CalendarApp } from "@calsync-react/calendar";
import "@calsync-react/calendar/styles.css";
function App() {
return (
<div className="h-screen w-screen">
<CalendarApp
locale="en"
preventOverlap={true}
defaultView="month"
licenseKey="YOUR_LICENSE_KEY"
/>
</div>
);
}Tip: In uncontrolled mode, events and agendas persist in the browser's localStorage automatically. No backend needed to get started.
Calendar Views
CalSync offers four built-in views. Users can switch between them using the toolbar tabs, or you can set the initial view via the defaultView prop.
Month View
Full month grid with event dots. Click a day to drill into it. Events are shown as colored badges.
Week View
7-day column layout with hourly time slots. Events are rendered as resizable, draggable blocks.
Day View
Single day with full time grid. Ideal for detailed scheduling and fine-grained event placement.
Year View
12-month overview showing event density per day. Great for spotting patterns across the whole year.
// Set initial view
<CalendarApp defaultView="week" />
// Set initial view + initial date
<CalendarApp
defaultView="day"
defaultDate={new Date(2025, 5, 15)} // June 15, 2025
/>Tip: Use onViewChange to track which view the user selected and persist it across sessions (e.g. in localStorage or URL params).
Event Management
CalSync supports full CRUD for events: create via modal, edit inline or via modal, drag to reschedule, resize to change duration, and delete.
Creating Events
Click the + button or click an empty time slot in week/day view. A modal opens with fields for description, event type, date, time, location, and agenda.
<CalendarApp
onEventCreate={(event) => {
console.log("New event:", event);
// event has: id, title, description, start, end, type, location, agendaId
await api.events.create(event);
}}
/>Editing Events
Click any event to open the edit modal. Changes are emitted via onEventUpdate.
<CalendarApp
onEventUpdate={(event) => {
// Fires on: modal save, drag-move, resize
await api.events.update(event.id, event);
}}
onEventClick={(event) => {
// Fires before the edit modal opens
analytics.track("event_clicked", { id: event.id });
}}
/>Deleting Events
<CalendarApp
onEventDelete={(eventId) => {
await api.events.delete(eventId);
}}
/>Agendas
Agendas act as color-coded categories for events (e.g. per team, per project, per department). Each event belongs to exactly one agenda. Users can toggle agendas on/off to filter the calendar.
Uncontrolled (default)
By default, agendas are stored in localStorage. Users create and delete them via the sidebar.
// CalSync manages agendas internally
<CalendarApp locale="en" />Controlled Agendas
Pass the agendas prop to manage them yourself. CalSync will not use localStorage for agendas when this prop is provided.
const [agendas, setAgendas] = useState<Agenda[]>([
{
id: "1",
name: "Team Alpha",
color: "#3b82f6",
isActive: true,
eventCount: 0,
createdAt: new Date(),
},
{
id: "2",
name: "Team Beta",
color: "#10b981",
isActive: true,
eventCount: 0,
createdAt: new Date(),
},
]);
<CalendarApp
agendas={agendas}
onAgendaCreate={(agenda) => {
setAgendas((prev) => [...prev, agenda]);
}}
onAgendaDelete={(agendaId) => {
setAgendas((prev) => prev.filter((a) => a.id !== agendaId));
}}
/>Note: When an agenda is deleted, all events linked to it are also removed. The onAgendaDelete callback fires after the user confirms the deletion in the built-in confirmation dialog.
Drag & Drop
CalSync uses @dnd-kit for smooth drag-and-drop interactions. Both week and day views support:
Drag to reschedule
Grab an event and move it to a new time slot or day. The onEventUpdate callback fires with the new start/end times.
Resize to change duration
Drag the bottom edge of an event to shorten or extend it. Works in both week and day views.
Overlap prevention
When preventOverlap is true (default), CalSync blocks drops that would cause two events to overlap on the same agenda.
<CalendarApp
preventOverlap={true} // block overlapping events (default)
onEventUpdate={(event) => {
// Fires after drag-move or resize completes
console.log(`Event ${event.id} moved to ${event.start}`);
}}
/>
// Allow overlapping events
<CalendarApp preventOverlap={false} />Availability Rules
Define color-coded availability blocks that render behind events in week and day views. When availability rules are set, events can only be created within those time slots. Overlapping rules on the same day are automatically prevented.
Visual availability blocks
Colored overlays render behind the time grid in week and day views, making it easy to see when scheduling is allowed.
Event creation enforcement
When rules exist, drag-to-create and modal-based event creation are blocked outside availability windows.
Overlap prevention
Adding a new rule that overlaps an existing one on the same day(s) is automatically rejected.
Sidebar management
Users can add and remove availability rules from the sidebar. Each rule has a label, days, time range, and color.
Uncontrolled (default)
By default, users manage availability rules via the sidebar UI. Use the onAvailabilityChange callback to sync changes to your backend.
<CalendarApp
onAvailabilityChange={(rules) => {
console.log("Updated rules:", rules);
// Save to your backend
await api.availability.save(rules);
}}
/>Controlled Availability
Pass the availabilityRules prop to manage rules externally.
import type { AvailabilityRule } from "@calsync-react/calendar";
const [rules, setRules] = useState<AvailabilityRule[]>([
{
id: "office-hours",
label: "Office Hours",
days: [1, 2, 3, 4, 5], // Mon–Fri
startHour: 9,
endHour: 17,
color: "bg-green-300",
},
{
id: "saturday-morning",
label: "Saturday Morning",
days: [6], // Saturday
startHour: 10,
endHour: 13,
color: "bg-blue-300",
},
]);
<CalendarApp
availabilityRules={rules}
onAvailabilityChange={(updated) => setRules(updated)}
/>Tip: If no availability rules are set, event creation is unrestricted. Once at least one rule exists, events can only be created within those time windows.
Recurring Events
CalSync supports recurring events with flexible frequency options. When creating or editing an event, users can enable recurrence and configure the pattern directly in the modal.
Daily
Repeats every N days. Example: every day, every 2 days.
Weekly
Repeats on specific days of the week. Example: every Monday and Wednesday.
Monthly
Repeats on the same day of the month. Example: the 15th of every month.
Yearly
Repeats on the same date each year. Example: January 1st every year.
Recurrence End Conditions
Each recurring event can end in one of three ways:
Never
The event repeats indefinitely.
After N occurrences
The event stops after a set number of repetitions (e.g. after 10 occurrences).
Until a date
The event repeats until a specific end date.
Programmatic Recurrence
When creating events programmatically in controlled mode, attach a recurrence object to the event:
import type { CalendarEvent, RecurrenceRule } from "@calsync-react/calendar";
const weeklyMeeting: CalendarEvent = {
id: "weekly-standup",
title: "Team Standup",
description: "Daily sync",
start: new Date(2025, 5, 16, 9, 0),
end: new Date(2025, 5, 16, 9, 30),
type: "Meeting",
recurrence: {
frequency: "weekly",
interval: 1,
byDay: [1, 3, 5], // Mon, Wed, Fri
count: 52, // 1 year of weekly meetings
},
};Note: Recurring events are rendered as badges with a frequency indicator in the agenda view. The recurrence pattern is fully editable from the event edit modal.
Timezone Support
CalSync supports IANA timezone strings. By default, it uses the browser's local timezone. Pass the timezone prop to override it.
// Use browser timezone (default)
<CalendarApp />
// Force a specific timezone
<CalendarApp timezone="America/New_York" />
<CalendarApp timezone="Europe/London" />
<CalendarApp timezone="Asia/Tokyo" />When a timezone is set, all events created will be tagged with that timezone internally. This is useful for applications where users operate across multiple time zones.
Tip: The timezone value is stored on each event object as event.timezone, so you can use it when syncing with external calendar systems (Google Calendar, Outlook, etc.).
Controlled Mode
For full control, pass events and/or agendas props. When provided, CalSync will not use localStorage for those items — you manage all state yourself.
import { useState } from "react";
import { CalendarApp } from "@calsync-react/calendar";
import type { CalendarEvent, ViewType } from "@calsync-react/calendar";
function App() {
const [events, setEvents] = useState<CalendarEvent[]>([]);
const [view, setView] = useState<ViewType>("month");
return (
<CalendarApp
events={events}
defaultView={view}
licenseKey="YOUR_LICENSE_KEY"
onEventCreate={(e) => {
setEvents((prev) => [...prev, e]);
// save to your DB here
}}
onEventUpdate={(e) => {
setEvents((prev) => prev.map((x) => (x.id === e.id ? e : x)));
}}
onEventDelete={(id) => {
setEvents((prev) => prev.filter((x) => x.id !== id));
}}
onViewChange={(v) => setView(v)}
/>
);
}Note: You can control events and agendas independently. For example, pass events to control events while letting CalSync manage agendas internally, or vice versa.
Callbacks
CalSync exposes granular callbacks for every user interaction. Use them to sync with your backend, analytics, or external state.
| Callback | When it fires |
|---|---|
| onEventCreate | User creates a new event via the modal |
| onEventUpdate | Event is edited, dragged, or resized |
| onEventDelete | User deletes an event |
| onEventClick | User clicks an event (before edit modal) |
| onAgendaCreate | User creates a new agenda |
| onAgendaDelete | User confirms agenda deletion |
| onAvailabilityChange | Availability rules are added or removed |
| onViewChange | User switches between month/week/day/year |
| onDateChange | User navigates to a different date |
Full example with all callbacks
<CalendarApp
licenseKey="CALSYNC-XXXX-XXXX-XXXX"
locale="en"
defaultView="week"
// Event callbacks
onEventCreate={(event) => api.events.create(event)}
onEventUpdate={(event) => api.events.update(event.id, event)}
onEventDelete={(id) => api.events.delete(id)}
onEventClick={(event) => console.log("Clicked:", event.title)}
// Agenda callbacks
onAgendaCreate={(agenda) => api.agendas.create(agenda)}
onAgendaDelete={(id) => api.agendas.delete(id)}
// Navigation callbacks
onViewChange={(view) => router.push(`?view=${view}`)}
onDateChange={(date) => router.push(`?date=${date.toISOString()}`)}
/>Settings
Configure the visible time range and initial state of the calendar.
Visible Time Range
In week and day views, use defaultSettings to limit the visible hours. By default, all 24 hours are shown (0–23).
// Show only business hours (8 AM – 6 PM)
<CalendarApp
defaultSettings={{ startHour: 8, endHour: 18 }}
defaultView="week"
/>
// Morning clinic (6 AM – 1 PM)
<CalendarApp
defaultSettings={{ startHour: 6, endHour: 13 }}
defaultView="day"
/>Environment Variable for License
Avoid hardcoding your license key. Use an environment variable instead.
# .env.local
NEXT_PUBLIC_CALSYNC_KEY=CALSYNC-XXXX-XXXX-XXXX<CalendarApp
licenseKey={process.env.NEXT_PUBLIC_CALSYNC_KEY}
/>Full Configuration Example
import { CalendarApp } from "@calsync-react/calendar";
import type { CalendarEvent, Agenda, ViewType } from "@calsync-react/calendar";
function MyCalendar() {
const [events, setEvents] = useState<CalendarEvent[]>([]);
const [agendas, setAgendas] = useState<Agenda[]>([]);
const [view, setView] = useState<ViewType>("week");
const [date, setDate] = useState(new Date());
return (
<CalendarApp
// License
licenseKey={process.env.NEXT_PUBLIC_CALSYNC_KEY}
// Appearance
locale="en"
defaultView={view}
defaultDate={date}
defaultSettings={{ startHour: 7, endHour: 20 }}
// Behaviour
preventOverlap={true}
// Controlled data
events={events}
agendas={agendas}
// Event callbacks
onEventCreate={(e) => setEvents((p) => [...p, e])}
onEventUpdate={(e) => setEvents((p) => p.map((x) => x.id === e.id ? e : x))}
onEventDelete={(id) => setEvents((p) => p.filter((x) => x.id !== id))}
// Agenda callbacks
onAgendaCreate={(a) => setAgendas((p) => [...p, a])}
onAgendaDelete={(id) => setAgendas((p) => p.filter((x) => x.id !== id))}
// Navigation callbacks
onViewChange={setView}
onDateChange={setDate}
/>
);
}Props API
Complete reference for all CalendarApp props.
| Prop | Type | Default | Description |
|---|---|---|---|
| events | CalendarEvent[] | undefined | Controlled events list. When provided, localStorage is NOT used for events. |
| agendas | Agenda[] | undefined | Controlled agendas list. When provided, localStorage is NOT used for agendas. |
| defaultView | "month" | "week" | "day" | "year" | "month" | Initial calendar view to display. |
| defaultDate | Date | today | Initial date the calendar is centered on. |
| defaultSettings | { startHour: number; endHour: number } | { startHour: 0, endHour: 23 } | Visible time range for week and day views. |
| locale | Locale | "fr" | Language for the UI. See Localization section. |
| preventOverlap | boolean | true | Prevent overlapping events on drag, resize, and create. |
| licenseKey | string | undefined | License key validated against the CalSync server. |
| customFields | CustomField[] | [] | Extra fields rendered in the event create/edit modals. Values stored in event.customData. |
| availabilityRules | AvailabilityRule[] | undefined | Controlled availability rules. Color-coded blocks shown in week/day views. |
| onAvailabilityChange | (rules: AvailabilityRule[]) => void | — | Fires when availability rules are added or removed. |
| timezone | string | browser TZ | IANA timezone string (e.g. "America/New_York"). Defaults to the browser timezone. |
| onEventCreate | (event: CalendarEvent) => void | — | Fires when a new event is created via the modal. |
| onEventUpdate | (event: CalendarEvent) => void | — | Fires when an event is moved, resized, or edited. |
| onEventDelete | (eventId: string) => void | — | Fires when an event is deleted. |
| onEventClick | (event: CalendarEvent) => void | — | Fires when an event is clicked. |
| onAgendaCreate | (agenda: Agenda) => void | — | Fires when a new agenda is created. |
| onAgendaDelete | (agendaId: string) => void | — | Fires when an agenda is deleted. |
| onViewChange | (view: ViewType) => void | — | Fires when the user switches calendar view. |
| onDateChange | (date: Date) => void | — | Fires when the user navigates to a different date. |
Types
TypeScript types exported from CalSync for use in your application.
CalendarEvent
type CalendarEvent = {
id: string;
title: string;
description: string;
start: Date;
end: Date;
type: string;
location?: string;
agendaId?: string;
customData?: Record<string, string | number | boolean>;
recurrence?: RecurrenceRule;
timezone?: string;
};AvailabilityRule
type AvailabilityRuleDay = 0 | 1 | 2 | 3 | 4 | 5 | 6; // Sun=0 … Sat=6
type AvailabilityRule = {
id: string;
label: string;
days: AvailabilityRuleDay[];
startHour: number; // 0–23 (decimals for half-hours, e.g. 9.5 = 9:30)
endHour: number;
color: string; // Tailwind bg class, e.g. "bg-green-300"
};RecurrenceRule
type RecurrenceFrequency = "daily" | "weekly" | "monthly" | "yearly";
type RecurrenceRule = {
frequency: RecurrenceFrequency;
interval?: number; // Every N days/weeks/months/years (default 1)
byDay?: number[]; // For weekly: days of week (0=Sun … 6=Sat)
count?: number; // Stop after N occurrences
until?: string; // Stop on this date (ISO string)
};CustomField
Define extra fields for the event create/edit modals via the customFields prop. Values are stored in event.customData.
type CustomFieldType = "text" | "number" | "select" | "textarea" | "checkbox" | "date" | "time";
interface CustomField {
name: string; // Unique key used in customData
label: string; // Display label
type: CustomFieldType;
required?: boolean;
placeholder?: string;
defaultValue?: string | number | boolean;
options?: { label: string; value: string }[]; // For "select" type
}Custom Fields Example
<CalendarApp
licenseKey="YOUR_KEY"
customFields={[
{ name: "patient", label: "Patient Name", type: "text", required: true, placeholder: "Enter patient name" },
{ name: "room", label: "Room", type: "select", options: [
{ label: "Room A", value: "a" },
{ label: "Room B", value: "b" },
]},
{ name: "notes", label: "Notes", type: "textarea", placeholder: "Additional notes..." },
{ name: "urgent", label: "Urgent", type: "checkbox" },
]}
onEventCreate={(event) => {
console.log(event.customData);
// { patient: "John Doe", room: "a", notes: "...", urgent: true }
}}
/>Agenda
type Agenda = {
id: string;
name: string;
color: string;
isActive: boolean;
eventCount: number;
createdAt: Date;
};ViewType
type ViewType = "month" | "week" | "day" | "year" | "agenda";Localization
CalSync ships with built-in translations for 10 languages. Set the locale prop to switch languages.
<CalendarApp locale="en" /> // English
<CalendarApp locale="fr" /> // French (default)
<CalendarApp locale="ja" /> // JapaneseLicense Key
CalSync requires a valid license key to unlock all features. Without a key, the component runs in a restricted demo mode.
Free Trial (7 days)
- • Full access to all features for 7 days
- • Up to 3 events during the trial period
- • License key sent to your email instantly
- • No credit card required
Pro License (€499, one-time)
- • Unlimited events and agendas
- • Lifetime updates and bug fixes
- • Priority email support
- • Commercial use license
Using your license key
<CalendarApp
licenseKey="CALSYNC-XXXX-XXXX-XXXX-XXXX"
locale="en"
defaultView="month"
/>How it works: On mount, the component sends your license key to the CalSync validation server. It returns the license status (valid, trial, expired) and enforces feature limits accordingly.
The validation happens once on component mount and is cached for the session.