Moltenship Time
Concepts

How it works

The packages, and how your events become a rendered view.

Packages

@moltenship/calendar                  what you import: everything below
├── @moltenship/calendar-react        Root, components, hooks, state
├── @moltenship/calendar-dnd          drag, resize, and create inside views
├── @moltenship/calendar-core         adapters, validation, snapshots, queries, changes
└── @moltenship/calendar-temporal     ranges, day boundaries, DST helpers

calendar-react and calendar-dnd build on calendar-core, which builds on calendar-temporal. calendar-core and calendar-temporal don't use React or the DOM, so they also run on a server or in a worker.

PackageUse it when
@moltenship/calendarAlmost always. It includes the packages below.
@moltenship/calendar-coreYou want to validate or query events in code that never loads React.
@moltenship/calendar-temporalYou only need the date and time zone helpers.
@moltenship/calendar-reactRarely. @moltenship/calendar already includes everything in it.

@moltenship/calendar has no "use client" directive of its own, so a Server Component can import its core and Temporal helpers. The React parts and the pointer layer keep their client boundary.

Import from the package name only. Files inside a package, such as @moltenship/calendar-core/src/events, aren't part of the API and can change at any time.

From events to the screen

  1. You pass events to Root.
  2. Root runs each record through your adapter, validates it, and builds a snapshot: a frozen, indexed copy of your events.
  3. Root asks the active view's strategy for the visible dates, then calls its project function with the snapshot. The result is the view's projection: the events it needs, in the shape it needs.
  4. ActiveView renders the view's Renderer with that projection.
  5. Buttons and your own controls call actions such as navigateNext or setView. The calendar updates uncontrolled state itself and sends controlled changes to your handlers. A new date or view means a new range and a new projection.

Steps 2 and 3 only run again when their inputs change. A new array with the same records in the same order reuses the snapshot, and records that are the same objects as before, or have an unchanged getRevision value, reuse their earlier result.

Using the engine without React

Snapshots work anywhere. This finds the meetings in a time range using calendar-core and calendar-temporal directly:

import { createEventSnapshotCache } from "@moltenship/calendar-core";
import { instantInterval } from "@moltenship/calendar-temporal";

const snapshots = createEventSnapshotCache(eventAdapter);
const snapshot = snapshots.getSnapshot(meetings);

const morning = snapshot.queryTimed(
  instantInterval(
    Temporal.Instant.from("2026-10-08T06:00:00Z"),
    Temporal.Instant.from("2026-10-08T10:00:00Z"),
  ),
);

morning.map((meeting) => meeting.title); // ["Standup"]

eventAdapter and meetings are the ones from the Quick start. The snapshot reference lists every query.

Edit on GitHub

Last updated on

On this page