Build with WTS Calendar
Render a calendar with a visible event, then add editing and connect your data. Choose your framework below. Standard examples run without an account or license key.
Open the event editor example, click a date to add an event, then click an event to edit it. Changes stay in your browser session.
Try the editable calendar →Already integrating? Open the API reference.
Looking for an exact option, method, route, or default?The generated API reference covers every client option and public calendar API, plus the complete PHP and ASP.NET Core route-level contracts.
Search the complete API reference →1. Build your first calendar
Web starters require Node.js 22.22.3 or a compatible version supported by your framework. If you already have an application, install the WTS packages from the command below and adapt the component files. The examples use published core 1.1.5.
npm create vite@latest my-calendar -- --template vanilla-ts
cd my-calendar
npm install
npm install @wts-calendar/core@1.1.5
npm run devCreate a Vite TypeScript project, then replace these files. The fixed view date keeps the sample event visible whenever you try it.
Or extract the starter, run npm install and npm run dev, then open the local address printed in your terminal.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My WTS Calendar</title>
</head>
<body>
<div id="calendar"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>import { WtsCalendar } from '@wts-calendar/core';
import './style.css';
const container = document.querySelector<HTMLElement>('#calendar');
if (!container) throw new Error('Missing calendar container');
const calendar = new WtsCalendar({
container,
...{ view: 'month' as const, viewDate: '2026-09-07', height: 'auto' as const },
events: [{ id: 'hello', title: 'Team planning', start: '2026-09-07T10:00:00', end: '2026-09-07T11:00:00' }],
});
// Vite cleanup runs only when this module is replaced during development.
if (import.meta.hot) import.meta.hot.dispose(() => calendar.destroy());
// In your app, call calendar.destroy() when removing its container.
@import '@wts-calendar/core/styles/calendar.css';
body { margin: 0; padding: 24px; font-family: system-ui, sans-serif; }
#calendar { max-width: 1000px; margin: auto; }The view and event dates intentionally match. Once it works, use your application’s dates and events. Browser wrappers need the global calendar stylesheet; native screens do not.
2. Make it useful for your product
Add and edit events
Use the built-in dialog for date clicks, selection, event updates, and validation.
Open the editor & framework code →Switch between month, week, and day
Add the optional TimeGrid module and choose the views in your calendar header.
Open TimeGrid & configuration →Load your events
Start with an events array, then load the visible date range from your own API.
Explore sources & caching →Plan people and resources Premium
See the package screenshot, integration code, and supported behavior before requesting a license.
Read the resource planning guide →Build booking flows Premium
Calculate slots, apply booking policies, assign staff, and coordinate appointment lifecycle actions through a headless API.
Read the booking integration guide →3. If your first calendar does not appear
- The grid has no styling
- Import the calendar CSS globally. Angular component-scoped styles do not replace the global import.
- The calendar appears, then disappears
- Call destroy() only when removing the calendar. Framework wrappers perform their own cleanup.
- The sample event is missing
- Keep viewDate and the sample event in the same month. Use the sample dates above before changing them.
- A week or day view is unavailable
- Import timeGridModule from @wts-calendar/core/time-grid and include it in plugins. The default entry includes month and DayGrid views.
- I see a browser-only or server-rendering error
- Mount the JavaScript core after its DOM element exists. In server-rendered React applications, place the calendar wrapper in a client component and follow your framework’s CSS loading rules.
4. Add a backend when your application needs one
Frontend wrappers render the calendar. The PHP and ASP.NET Core packages implement the same server-side event REST contract while your application keeps control of authentication, authorization, and durable storage.
composer require wts-calendar/server-php:^1.0 nyholm/psr7<?php
use Nyholm\Psr7\Factory\Psr17Factory;
use WtsCalendar\Server\CalendarApiHandler;
use WtsCalendar\Server\CalendarApiOptions;
use WtsCalendar\Server\CalendarEventStoreInterface;
$psr17 = new Psr17Factory();
$handler = new CalendarApiHandler(
$container->get(CalendarEventStoreInterface::class),
$psr17,
$psr17,
new CalendarApiOptions(
requireIfMatchForUpdate: true,
requireIfMatchForDelete: true,
),
);
// $request is the host application's PSR-7 ServerRequestInterface.
$response = $handler->handle($request);Use any PSR-7 request, PSR-17 factories, and a durable CalendarEventStoreInterface implementation owned by your application.
Shared HTTP contract
| Method | Route | Purpose |
|---|---|---|
GET | /api/calendar/events?start=...&end=... | Load a bounded visible range |
GET | /api/calendar/events/{id} | Load one event and its ETag |
POST | /api/calendar/events | Create an event |
PATCH / PUT | /api/calendar/events/{id} | Update with If-Match conflict protection |
DELETE | /api/calendar/events/{id} | Delete with an optional version precondition |
The server package handles
- Typed calendar event requests and responses
- Range validation, CRUD routes, and RFC 7807 errors
ETagandIf-Matchoptimistic concurrency- A replaceable storage interface
Your application still handles
- Authentication and event-level authorization
- Database schema, migrations, and tenant isolation
- CORS origins, rate limits, secrets, and monitoring
- Production storage; in-memory stores are demo-only
Connect every frontend to the same endpoint
Angular, React, and Vue use the core REST adapter. It carries mutation versions through ETag and If-Match so the server can reject stale edits. React Native calls the same authenticated JSON endpoint and passes the resulting events to its native component.
import {
CalendarDataClient,
createRestCalendarDataAdapter,
} from '@wts-calendar/core/data-adapter-sdk';
const endpoint = 'https://api.example.com/api/calendar/events';
export const calendarEvents = new CalendarDataClient(
createRestCalendarDataAdapter({
url: endpoint,
mutationUrl: ({ type, id }) =>
type === 'create'
? endpoint
: `${endpoint}/${encodeURIComponent(id ?? '')}`,
headers: async () => ({
authorization: `Bearer ${await accessToken()}`,
}),
}),
);import { useEffect, useState } from 'react';
import { WtsCalendarNative } from '@wts-calendar/react-native';
import type { NativeCalendarEventInput } from '@wts-calendar/react-native';
const endpoint = 'https://api.example.com/api/calendar/events';
export function Schedule({ accessToken }: { accessToken: string }) {
const [events, setEvents] = useState<readonly NativeCalendarEventInput[]>([]);
useEffect(() => {
const query = new URLSearchParams({
start: '2026-09-01T00:00:00Z',
end: '2026-10-01T00:00:00Z',
timeZone: 'UTC',
});
fetch(`${endpoint}?${query}`, {
headers: { authorization: `Bearer ${accessToken}` },
})
.then((response) => {
if (!response.ok) throw new Error('Calendar request failed');
return response.json();
})
.then((page) => setEvents(page.records));
}, [accessToken]);
return <WtsCalendarNative events={events} />;
}5. Explore one feature at a time
The examples directory shows the feature options and runtime behavior together. Optional modules are loaded only for the relevant examples.
Open examples →Sample events are stored in memory and reset when you reload or switch examples. This demo does not ask for provider credentials, store customer events, or run premium integrations.