Skip to content

Calendar

Documentation · Integration · Gantt

Month, week, day and agenda scheduling for Vanilla JavaScript, React and Vue 3.

Contents

Vanilla JavaScript

Provide a host element, for example <div id="calendar"></div>:

js
import { createCalendar } from '@xsigns/chronosketch'
import '@xsigns/chronosketch/styles.css'

const host = document.querySelector('#calendar')
if (!(host instanceof HTMLElement)) throw new Error('Calendar host is missing')

const calendar = createCalendar({
    date: '2026-09-17',
    view: 'month',
    locale: 'de-DE',
    events: [
        { id: 'workshop', title: 'Workshop', start: '2026-09-17', end: '2026-09-20' },
        {
            kind: 'timed',
            id: 'review',
            title: 'Review',
            start: '2026-09-18T10:00',
            end: '2026-09-18T11:30',
        },
    ],
    onEventClick: event => console.log('Selected event', event),
    onDateClick: date => console.log('Selected date', date),
    onEventDrop: ({ previous, event }) => console.log('Moved', previous, event),
    onEventResize: ({ previous, event }) => console.log('Resized', previous, event),
})

calendar.mount(host)

// Later:
// calendar.setEvents(nextEvents)       // replaces the entire collection
// calendar.setDate('2026-10-01')
// calendar.setView('week')
// calendar.destroy()                  // call when your host is removed

Each instance has independent state. Mount in a visible, styled container so initial time-grid scrolling can measure the layout. Imports are SSR-safe; mounting requires the browser DOM. Destroying removes ChronoSketch's own subtree and subscription; unrelated host children remain.

React

Pass options as individual props. Keep events in application state and replace the edited event on drop/resize:

tsx
import { useState } from 'react'
import { Calendar } from '@xsigns/chronosketch/react'
import type { CalendarEvent, CalendarEventMove } from '@xsigns/chronosketch'
import '@xsigns/chronosketch/styles.css'

const initialEvents: readonly CalendarEvent[] = [
    {
        kind: 'timed',
        id: 'review',
        title: 'Review',
        start: '2026-09-18T10:00',
        end: '2026-09-18T11:30',
    },
]

export function BookingCalendar() {
    const [events, setEvents] = useState(initialEvents)

    function acceptChange({ event }: CalendarEventMove) {
        setEvents(current => current.map(item => item.id === event.id ? event : item))
    }

    return (
        <div className="booking-calendar">
            <Calendar
                date="2026-09-18"
                view="week"
                locale="de-DE"
                events={events}
                onEventDrop={acceptChange}
                onEventResize={acceptChange}
                onEventClick={event => console.log(event)}
            />
        </div>
    )
}

The adapter owns mount/unmount cleanup, including React StrictMode. Replace event arrays rather than mutating them. Keep locales objects stable: changing locale or locales recreates the instance and may reset local navigation.

Vue 3

Pass one options object. Callbacks are fields of that object:

vue
<script setup lang="ts">
import { shallowRef } from 'vue'
import { Calendar } from '@xsigns/chronosketch/vue'
import type { CalendarEventMove, CalendarOptions } from '@xsigns/chronosketch'
import '@xsigns/chronosketch/styles.css'

function acceptChange({ event }: CalendarEventMove) {
    options.value = {
        ...options.value,
        events: (options.value.events ?? []).map(item => item.id === event.id ? event : item),
    }
}

const options = shallowRef<CalendarOptions>({
    date: '2026-09-18',
    view: 'week',
    locale: 'de-DE',
    events: [
        {
            kind: 'timed',
            id: 'review',
            title: 'Review',
            start: '2026-09-18T10:00',
            end: '2026-09-18T11:30',
        },
    ],
    onEventDrop: acceptChange,
    onEventResize: acceptChange,
    onEventClick: event => console.log(event),
})
</script>

<template>
    <div class="booking-calendar">
        <Calendar :options="options" />
    </div>
</template>

Vue watches events deeply, but replacing the collection gives explicit ownership. The adapter cleans up on unmount. Locale changes recreate its instance with the previous snapshot and supplied options. Your application owns Vue's bundler configuration; the demo supplies compile-time feature flags in demo/web/vite.config.ts.

Event data

These exported types describe supported input:

ts
type DateKey = string // canonical YYYY-MM-DD

interface AllDayCalendarEvent {
    readonly kind?: 'all-day'
    readonly id: string
    readonly title: string
    readonly start: DateKey
    readonly end: DateKey
}

interface TimedCalendarEvent {
    readonly kind: 'timed'
    readonly id: string
    readonly title: string
    readonly start: string // YYYY-MM-DDTHH:mm
    readonly end: string   // YYYY-MM-DDTHH:mm
}

type CalendarEvent = AllDayCalendarEvent | TimedCalendarEvent
RuleMeaning
Exclusive endIntervals are [start, end). September 17–20 occupies September 17, 18 and 19.
One all-day dateUse start: '2026-09-17', end: '2026-09-18'.
Explicit timed kindSet kind: 'timed'; omitted kind means all-day.
Timed valuesFloating local date/time, minute precision. No seconds, Z or UTC offset.
MidnightEnd at next-day T00:00 does not occupy that next date.
ValidationValid Gregorian dates, end after start, non-empty IDs unique within the collection. Overlaps are allowed.
TextTitles render as text, not HTML.
StateEvents are copied, frozen and sorted by start then ID. Replace them through the API.

Floating means 10:00 stays 10:00 wherever displayed. ChronoSketch does not convert time zones or resolve daylight-saving gaps/overlaps. Do not derive civil dates by slicing Date.toISOString() unless UTC is your intended date.

Map backend records to this shape at your application boundary. Keep business records and permissions in your own model, keyed by event ID. Callbacks receive the whole calendar event, including its original interval when multi-day bars are split into visible segments. Use typed presentation and templates for product metadata, separate content fields, palettes and status.

setEvents replaces the entire collection; it does not append, merge or fetch. Invalid event replacements throw before replacing the snapshot. TypeScript declarations are not a comprehensive runtime schema validator for arbitrary untrusted objects.

Options reference

All CalendarOptions fields are optional. The same options apply to the factory, React props and Vue's options.

OptionType / accepted valuesDefault and behavior
width, heightnumber | stringUnset; explicit outer dimensions
minWidth, maxWidth, minHeight, maxHeightnumber | stringUnset; CSS size constraints
dateDateKeyClient's current civil date; navigation anchor
view'month' | 'week' | 'day' | 'agenda'First enabled view; normally month
viewsReadonly array of view keysAll four views in the above order; controls availability/order
eventsreadonly CalendarEvent[]Empty array
localeBCP 47 string'de-DE'
localesCalendarLocalesNo overrides; Intl and built-in messages
controlsStyle'squared' | 'rounded' | 'flat''rounded'
showWeekNumbersbooleanfalse; ISO week numbers
weekScrollPosition'current-time' | 'midnight' | 'first-event''current-time'
dayScrollPosition'current-time' | 'midnight' | 'first-event''current-time'; independent of Week
iconSet'material' | 'font-awesome''material'
iconsCalendarIconsPer-slot overrides
interactionsCalendarInteractionsdraw, move, resize: each defaults to true
eventActionsCalendarEventActionsNo context actions
onEventClick(event: CalendarEvent) => voidNo handler
onDateClick(date: DateKey) => voidNo handler
onEventDrop(change: CalendarEventMove) => voidOptional post-move notification
onEventResize(change: CalendarEventMove) => voidOptional post-resize notification
onEventCreateRequested(request: CalendarEventCreationRequest) => voidAbsent: creation gestures disabled

There is no separate defaultView, dark, theme, shadow, CSS-path or color option. Initial view uses view; appearance beyond control presets uses the CSS contract.

Views and navigation

The Day all-day area shows existing events without a date-number button. When empty, it displays “Keine ganztägigen Termine” / “No all-day events”, overridable with messages.emptyAllDay. Clicking or drawing in this area does not request creation. Use the explicit New event toolbar action for an all-day request.

ViewDisplayPrevious / next
monthSix weeks including adjacent-month dates; connected event bars per rowOne month
weekSeven-day all-day header and scrollable 24-hour rasterSeven days
dayOne day's all-day area and scrollable 24-hour rasterOne day
agendaEvents grouped by occupied date in the selected monthOne month

All-day bars are filled; timed bars in Month are outlined. Multi-day Month events and weekly all-day events stay connected within each visible week. Week boundaries create continuation segments. Overlapping date bars use separate lanes; rows grow to fit them.

Use views: ['week', 'day'] to hide Month and Agenda and start in Week, or add view: 'day' to start in Day. Duplicate views are collapsed; empty lists and unknown keys are rejected. A supported initial view excluded by views falls back to the first enabled view. Calling setView with a disabled view throws.

Today navigates to the client's date. The date button opens a localized picker anchored near the field on desktop and a modal overlay on mobile. Selecting a picker date navigates the calendar. onDateClick is a date-cell interaction callback, not a general navigation notification.

showWeekNumbers adds a leading Month column with one KW/Week heading and numbers below, a suffix in Week/Day titles and week numbers beside Agenda dates. These are ISO weeks regardless of locale: week one contains January 4. For a displayed week with a different starting weekday, its Thursday identifies the ISO week. The picker has no week-number column.

Time grids initially scroll to the client's current time, midnight, or the earliest visible timed segment (first-event, falling back to 08:00). The same policies are available on iOS. This is initial positioning, not live clock following. Manual scroll survives event updates within the same visible period; changing the day/week reapplies the policy. Narrow Week layouts scroll horizontally.

Editing and creation matrix

InteractionMonthWeekDayAgenda
Move to another dateBoth event kindsAll-day bars and eligible timed cardsNo date dragNo
Move a timed event verticallyNoYesYesNo
Resize a timed event at its bottom edgeNoYesYesNo
Draw an all-day rangeYesYes, all-day headerNoNo
Click empty all-day area to createNo; draw insteadYesNoNo
Draw a timed intervalNoYes, within one dayNoNo
Event click / context actionsYesYesYesYes

Existing-event drag and resize are available without notification callbacks. Creation requires onEventCreateRequested.

Timed editing moves full original intervals, including overnight events from their visible segments. Week supports horizontal and diagonal movement to other visible dates. Movement preserves duration; bottom resizing preserves start and requires a positive duration. Both operations use relative 15-minute deltas, retaining off-grid minutes. Invalid targets are rejected; a moved segment start must remain inside the target day, but the original duration may extend beyond midnight. Only a segment containing the actual exclusive end offers end resizing.

Drawing and editing currently use the mouse. Escape, outside release, scroll, window blur, pointer cancellation or calendar replacement cancel an active preview. Automatic scrolling during gestures is not implemented.

Callbacks and state ownership

ts
import type { CalendarEvent } from '@xsigns/chronosketch'

interface CalendarEventMove {
    readonly previous: CalendarEvent
    readonly event: CalendarEvent
}

onEventDrop and onEventResize run after local calendar state changes. They contain complete immutable before/after events. They are notifications, not cancellable pre-save hooks; return values are ignored.

Your product owns persistence, errors, permissions and conflict handling. Mirror accepted edits into your authoritative collection, as in the React/Vue examples. A later event prop replacement overwrites local calendar edits. For failed saves, supply your authoritative collection again, accounting for edits made while the request was pending.

Vanilla consumers can read calendar.getSnapshot().events after a change. If using an external store, update it in the callback and supply its array through setEvents. Catch rejected persistence promises in your application; the library does not await them.

Programmatic moveEvent, moveTimedEvent and resizeTimedEvent return the change and notify store subscribers, but do not invoke UI drop/resize callbacks. Date/event clicks do not persist anything. React/Vue synchronize supplied date/view changes while toolbar navigation remains local; there are no onDateChange/onViewChange controlled-state callbacks yet.

Creating events

ChronoSketch selects the interval; your product supplies the form, title, ID, validation and storage.

ts
import type { DateKey } from '@xsigns/chronosketch'

type CalendarEventCreationRequest =
    | { readonly kind: 'all-day'; readonly start: DateKey; readonly end: DateKey }
    | { readonly kind: 'timed'; readonly start: string; readonly end: string }

Requests contain no title, ID, browser event or framework object. Both variants have exclusive ends and use the same formats as stored events.

  • Month: press a free date area or number and drag. A connected preview appears immediately; moving at least four pixels and releasing inside the grid requests the inclusive selected dates. Reverse and cross-row drawing work. A plain date click does not create.
  • Week all-day: click a free cell/date button for one day, or drag across dates for a connected multi-day preview and one request.
  • Week/Day timed: draw vertically in empty raster space. Boundaries snap to the nearest quarter hour, reverse drawing is normalized and minimum duration is 15 minutes. Release in another day column cancels. A plain raster click does not create.

A localized Add button also requests the selected all-day date in every view.

The preview disappears before the callback. No event is inserted automatically, and returning an event or Promise does not insert one.

Product-owned asynchronous creation

This helper leaves dialog and persistence implementation with your application:

ts
import { createCalendar } from '@xsigns/chronosketch'
import type { CalendarEvent, CalendarEventCreationRequest } from '@xsigns/chronosketch'

interface ProductEventWorkflow {
    openDialog(request: CalendarEventCreationRequest): Promise<CalendarEvent | undefined>
    save(event: CalendarEvent): Promise<CalendarEvent>
    showError(error: unknown): void
}

export function createBookingCalendar(workflow: ProductEventWorkflow) {
    const calendar = createCalendar({
        onEventCreateRequested: request => {
            void createRequestedEvent(request).catch(error => workflow.showError(error))
        },
    })

    async function createRequestedEvent(request: CalendarEventCreationRequest) {
        const draft = await workflow.openDialog(request)
        if (!draft) return
        const saved = await workflow.save(draft)
        // Read after awaiting: preserve events changed while the dialog was open.
        calendar.setEvents([...calendar.getSnapshot().events, saved])
    }

    return calendar // mount/destroy this instance in your host lifecycle
}

Branch on request.kind when choosing form fields. Generate IDs in your product or backend. Manage concurrent dialogs, duplicate saves and host disposal in your product workflow.

In React append with setEvents(current => [...current, saved]). In Vue replace options.value.events from its current collection. Cancelling leaves events unchanged. The demo immediately adds a named event; that is sample application behavior, not a built-in editor.

Context actions

Supply a static array or a function resolved for the activated event:

ts
import type { CalendarEventActions } from '@xsigns/chronosketch'

const eventActions: CalendarEventActions = event => [
    {
        id: 'inspect',
        label: 'Details',
        onSelect: selected => console.log('Open product dialog', selected),
    },
    {
        id: 'archive',
        label: 'Archive',
        disabled: event.id === 'locked',
        onSelect: selected => console.log('Product archive action', selected.id),
    },
]

Each action has id: string, label: string, optional disabled: boolean and onSelect: (event: CalendarEvent) => void. Pass eventActions as an option. Labels and permission decisions belong to your application.

Right click, Shift+F10 or touch long press (550 ms) opens the menu. Empty actions produce no menu; disabled actions do not run. Callbacks receive the full event rather than a rendered segment. Displaying or disabling an action is not backend authorization.

Locale configuration

locale chooses formatting and messages; locales is an object keyed by BCP 47 identifiers, not an array. An exact canonical key takes precedence over a language-only key; they are not merged with one another.

ts
import type { CalendarLocales } from '@xsigns/chronosketch'

const locales = {
    'de-DE': {
        calendar: {
            days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
            daysShort: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
            months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
            monthsShort: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
            firstDayOfWeek: 1,
            format24h: true,
        },
        messages: { today: 'Heute', newEvent: 'Neuer Termin', weekNumber: 'KW' },
    },
    'en-US': {
        calendar: { firstDayOfWeek: 0, format24h: false },
    },
} satisfies CalendarLocales

// createCalendar({ locale: 'de-DE', locales })

All override fields are optional. Day arrays contain seven non-empty names in Sunday-first order even when the week starts Monday. Month arrays contain twelve names starting January. firstDayOfWeek uses 0 for Sunday through 6 for Saturday. format24h overrides locale hour formatting.

Without overrides, dates and times use Intl with the Gregorian calendar. German locale identifiers use German controls; other locales use English controls unless overridden. Week start comes from Intl week information; its compatibility fallback is Sunday for US locales and Monday otherwise. Omitted hour format follows the locale.

Supported messages keys:

text
newEvent, weekNumber, view, week, day, allDay, emptyAllDay, dayTimeGrid,
editTimedEvent, timeGrid, previousWeek, nextWeek, previousDay, nextDay,
today, month, agenda, previous, next, empty, calendar, chooseDate, close

Calendar and picker share names and week start. The date button uses localized numeric formatting. There is no editable masked input, Luxon/Quasar configuration or arbitrary format-pattern option. See locale behavior.

Editing interactions

Disable editing gestures independently using interactions:

ts
const calendar = createCalendar({
    interactions: {
        draw: false,
        move: true,
        resize: false,
    },
})
// Replace the flags later; omitted flags default to true:
calendar.setInteractions({ move: false })
calendar.setInteractions() // Restore all defaults

All three flags default to true. Drawing still requires onEventCreateRequested. draw: false also disables Week's all-day click-to-create. move covers Month date dragging, weekly all-day dragging and timed Week/Day movement. resize controls timed Week/Day duration changes; disabled resizing removes the bottom grip. Each flag works independently.

React accepts interactions as a prop; Vue accepts it inside options. Updates cancel active gestures without committing them and preserve calendar state. Navigation, date/event clicks and consumer context actions remain available. Programmatic commands such as moveEvent, moveTimedEvent, resizeTimedEvent and setEvents remain usable. These are UI capabilities, not authorization rules. See interaction details.

Component sizing

Calendar and Timeline accept the same optional sizing fields:

width, height, minWidth, maxWidth, minHeight, maxHeight. Numbers mean CSS pixels; strings accept CSS dimensions such as '100%', '70vh' or 'calc(100vh - 120px)'. Dimensions include padding and borders. Without sizing options, the existing responsive defaults remain in effect.

ts
import { createCalendar } from '@xsigns/chronosketch'

const calendar = createCalendar({
    width: '100%',
    maxWidth: 1000,
    height: 600,
    minHeight: 300,
})
calendar.setSize({ width: '100%', height: '70vh', maxHeight: 800 })
// Reset all explicit dimensions:
calendar.setSize()

Pass these fields to createTimeline alongside its required resources, range and loadData options. React accepts them as props; Vue accepts them inside options. Both adapters synchronize size changes without recreating the instance.

A height or maximum height makes the content scroll within the component. Percentage heights require a parent with a definite height. setSize replaces all six sizing fields: omitted fields are cleared, rather than retained. See sizing details for validation and layout behavior.

Styling and themes

Import package CSS first and your overrides afterward. Scope styles to the actual .xc-calendar inside a wrapper you own:

css
.booking-calendar .xc-calendar {
    --xc-accent: #006b54;
    --xc-on-primary: #ffffff;
    --xc-primary-container: #c0f2dc;
    --xc-on-primary-container: #00382b;
    --xc-surface: #ffffff;
    --xc-on-surface: #18251f;
    --xc-surface-container: #edf5ef;
    --xc-border: #bfd0c4;
    --xc-muted: #52645a;
    --xc-radius-calendar: 12px;
    --xc-shadow-calendar: 0 4px 18px #18251f18;
}

Set related foreground/background roles together. Variables declared only on the wrapper do not override defaults declared directly on .xc-calendar.

CustomizationUse
All control shapescontrolsStyle: rounded 24px, squared 4px, flat 4px with transparent resting surfaces
Outer component corners--xc-radius-calendar (default 16px)
Outer component shadow--xc-shadow-calendar; use none to disable
Main / adjacent-month surfaces--xc-surface / --xc-surface-container
Borders--xc-border, --xc-border-width
Selection and todayPrimary/accent variables and documented date-state selectors
Targeted layout or state overridesDocumented .xc-* classes

Today's indicator always stays circular: filled with squared/rounded controls and outlined in flat mode. The outer container, menus and event cards have independent radii. The date-trigger icon is 18px; shared navigation icons remain 24px.

ChronoSketch uses custom Material selection popups rather than native select option menus. There is no Shadow DOM. Menus and the picker remain descendants of the calendar, so instance-scoped themes also reach them.

For Vue SFC scoped CSS, use :deep(.xc-calendar) and corresponding deep selectors, or globally loaded CSS scoped by your wrapper.

System, light and dark appearance

Theme mode is application-owned CSS. The demo defaults to System, follows prefers-color-scheme live and allows Light/Dark overrides. Reproduce this with a default light palette, a dark palette under a media query for System, and explicit wrapper selectors for forced modes. No mode option is passed to ChronoSketch.

The complete variable table, state selectors and dark-theme example are in the shipped CSS reference. Classes are styling hooks, not a stable JavaScript DOM-query or template API.

Icons

A selected subset of Material Icons and Font Awesome Free ships as inline SVGs. No external font or CDN is required; the full upstream libraries are not bundled.

ts
import type { CalendarOptions } from '@xsigns/chronosketch'

const iconOptions = {
    iconSet: 'material',
    icons: {
        previous: 'material:arrow_back',
        next: 'material:arrow_forward',
        datePicker: 'font-awesome:calendar-days',
    },
} satisfies CalendarOptions

Slots are previous, next, datePicker, viewSelect and selection. Overrides may mix sets. Names are restricted to the exported CalendarIconName union; arbitrary HTML/SVG strings are not supported.

See the icon catalog and defaults, third-party notices and included licenses.

Instance methods and headless store

createCalendar(options?) returns these methods. Framework adapters manage their own instance; update their props/options instead.

MethodContract
mount(target: HTMLElement)Mount owned subtree; dispose any previous mount
destroy()Remove UI/subscription; store remains usable and remountable
getSnapshot()Readonly { date, view, views, events }
subscribe(listener: () => void)Synchronous state notifications; returns unsubscribe
setInteractions(value?: CalendarInteractions)Replace gesture flags; omitted flags are enabled
setSize(size?: ComponentSize)Replace all size fields; omit argument to reset
setDate(date: DateKey)Set navigation date
setView(view: CalendarView)Select an enabled view
setViews(views?, preferredView?)Keep current/preferred if enabled, otherwise first; omission restores all views
setEvents(events)Validate and replace all events
navigate(steps: number)Move by active view's period; use integer steps
goToToday()Navigate to client's current civil date
moveEvent(id, fromDate, toDate)Shift either kind by date difference, preserving interval and timed clock values
moveTimedEvent(id, minutes)Shift both endpoints by an integer minute delta
resizeTimedEvent(id, minutes)Shift end only by an integer minute delta
setControlsStyle(style?)Change preset; omission resets rounded
setShowWeekNumbers(value?)Change visibility; omission resets false
setWeekScrollPosition(position?)Change week policy; omission resets current-time
setDayScrollPosition(position?)Change day policy; omission resets current-time
setIcons(overrides?, set?)Replace configuration; omission restores Material defaults
setEventActions(actions?)Replace list/resolver; omission removes actions
setEventCreationHandler(callback?)Replace callback; omission disables creation

Movement/resizing returns CalendarEventMove | undefined; zero movement returns undefined. fromDate must be occupied by the event. Invalid IDs or intervals throw. Programmatic minute commands do not apply UI snapping, minimum duration or single-day bounds; resulting duration must still be positive.

Presentation setters do not notify application-state subscribers. There is no generic setOptions or Vanilla setLocale; recreate the instance for locale changes, preserving its snapshot if needed.

Store without the DOM renderer

ts
import { createCalendarStore } from '@xsigns/chronosketch/core'

const store = createCalendarStore({ date: '2026-09-17', view: 'week' })
const unsubscribe = store.subscribe(() => console.log(store.getSnapshot()))

store.navigate(1)
store.setEvents([
    { id: '1', title: 'Workshop', start: '2026-09-24', end: '2026-09-25' },
])

unsubscribe()

The store exposes state/query/navigation/movement methods, not mounting or presentation setters. Its public options type is CalendarOptions for compatibility, but only date, view, views and events affect it. It does not render or execute UI callbacks.

Exported helpers and types

The root and /core exports also provide:

HelperBehavior
today()Client-local DateKey
parseDate(value)Validate civil date; return a UTC-noon Date carrier, not an event instant
formatDate(date)Serialize the UTC date portion of a Date
addDays(value, amount)Civil day arithmetic
addMonths(value, amount)Civil month arithmetic, clamping destination day
monthDays(value, firstDayOfWeek = 1)42 civil dates; explicit weekday 0–6
validateEvents(events)Validate, copy, freeze and sort events
eventsOnDate(events, date)Select events occupying a date with exclusive-end semantics

Public types: CalendarInteractions, ComponentSize, ComponentDimension, DateKey, CalendarEvent, AllDayCalendarEvent, TimedCalendarEvent, CalendarEventMove, CalendarEventCreationRequest, AllDayEventCreationRequest, TimedEventCreationRequest, CalendarView, CalendarSnapshot, CalendarOptions, CalendarControlsStyle, CalendarLocaleConfiguration, CalendarLocales, CalendarEventAction, CalendarEventActions, CalendarIconName, CalendarIcons, CalendarIconSet and CalendarIconSlot.

Current limitations and native platforms

  • The calendar has no persistence, built-in event form, recurrence, resource lanes or asynchronous range loading. Client-side availability/conflict checks, blocked intervals and buffers are supported through the availability contract. Resource rows and lazy loading belong to the separate Timeline component.
  • No zoned scheduling or daylight-saving resolution.
  • Very dense timed overlap groups produce narrow columns; there is no overflow menu.
  • No touch/keyboard drag or resize, gesture auto-scroll, configurable snapping or per-event editing permissions.
  • No Day/Agenda creation gestures or complete controlled-state contract.
  • Locale changes can recreate framework instances. Rendering replaces the calendar subtree; scroll is preserved within a period, but event focus across layout changes is not guaranteed.
  • No complete accessibility certification or published browser/performance support matrix.

Web provides Vanilla, React and Vue integrations. A separate SwiftUI integration provides native iOS Calendar; Android remains planned. The npm package is not a native widget. Functional parity is incomplete: native availability rules, custom Calendar content and context actions are absent, and editing semantics differ.

Event presentation API

CalendarOptions<TMetadata> adds eventPresentation, eventRenderer and eventTheme. The instance exposes setEventPresentation, setEventRenderer and setEventTheme. Vue provides the event slot and createCalendarComponent<TMetadata>(); React provides renderEvent through CalendarProps<TMetadata>. See the complete presentation, template and overlap guide.

Optional consumer-confirmed edits

onEventMoveRequested(change) and onEventResizeRequested(change) opt their respective UI operation into a controlled flow. change retains the existing { previous, event } shape. The library validates availability and emits the proposal without changing confirmed events. Confirm with setEvents or updated React/Vue event data. Reject/ignore to retain current data. Your application owns stale-snapshot validation and persistence.

When a request callback is present, that operation does not run the optimistic update or its legacy onEventDrop/onEventResize notification. Omitting the request callback retains the existing post-update behavior for compatibility. Interaction flags remain independent. setEventEditHandlers({...}) replaces request handlers; React/Vue synchronize the corresponding options. Programmatic store mutation methods retain their documented direct-update behavior.

ChronoSketch · Calendar and scheduling components