DOCUMENTATION / QUICKSTART

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.

Try the result before installing.

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.

Install command
npm create vite@latest my-calendar -- --template vanilla-ts
cd my-calendar
npm install
npm install @wts-calendar/core@1.1.5
npm run dev

Create a Vite TypeScript project, then replace these files. The fixed view date keeps the sample event visible whenever you try it.

Download JavaScript starter →

Or extract the starter, run npm install and npm run dev, then open the local address printed in your terminal.

index.html
<!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>
src/main.ts
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.
src/style.css
@import '@wts-calendar/core/styles/calendar.css';

body { margin: 0; padding: 24px; font-family: system-ui, sans-serif; }
#calendar { max-width: 1000px; margin: auto; }
Expected result: September 2026 with “Team planning” on September 7.

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.

Read JavaScript setup guide ↗

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.
Search options and defaults →

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 install
composer require wts-calendar/server-php:^1.0 nyholm/psr7
Framework-neutral PHP handler
<?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

MethodRoutePurpose
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/eventsCreate 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
  • ETag and If-Match optimistic 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.

Angular, React and Vue data adapter
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()}`,
    }),
  }),
);
React Native API loading
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 →
Runtime-only by design.

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.