Headless Core
The trud-calendar-core package provides all calendar logic with zero dependencies and zero React dependency. Use it to build calendar UIs in any framework — Vue, Svelte, Angular, vanilla JS, or server-side.
Installation
Section titled “Installation”npm install trud-calendar-coreWhat’s included
Section titled “What’s included”All TypeScript interfaces and type definitions:
import type { CalendarEvent, CalendarView, CalendarState, CalendarAction, CalendarConfig, CalendarSlots, CalendarLocale, CalendarLabels, DateString, DateTimeString, PositionedEvent, EventSegment, RecurrenceRule, RecurrenceDay, TimedEventSegment, UndoStack, VirtualRange,} from "trud-calendar-core";Date utilities
Section titled “Date utilities”Pure functions for date math — no side effects, fully tree-shakeable:
import { parseDate, toDateString, toDateTimeString, addDays, addMonths, addWeeks, startOfWeek, startOfMonth, endOfMonth, isSameDay, isSameMonth, isToday, isBefore, isAfter, rangesOverlap, dateInRange, daysBetween, eachDayOfRange, getWeekDays, getMonthViewRange, getWeekViewRange, getVisibleRange, getTimeOfDay, getDurationHours, getHourLabels,} from "trud-calendar-core";Formatting
Section titled “Formatting”Locale-aware formatting via the Intl API (memoized formatters):
import { formatToolbarTitle, formatWeekdayShort, formatWeekdayNarrow, formatDayNumber, formatTime, formatTimeRange, formatAgendaDate, formatMonthDay,} from "trud-calendar-core";
formatTime("2026-03-13T14:30:00", "en-US"); // "2:30 PM"formatTime("2026-03-13T14:30:00", "de-DE"); // "14:30"formatAgendaDate("2026-03-13", "es-ES"); // "viernes, 13 de marzo"formatToolbarTitle("2026-03-13", "month", "ja-JP"); // "2026年3月"Event utilities
Section titled “Event utilities”Sorting, filtering, layout algorithms:
import { sortEvents, filterEventsInRange, getEventsForDay, isMultiDayEvent, partitionEvents, segmentMultiDayEvent, segmentTimedMultiDayEvent, getEventSegments, buildOverlapGroups, assignColumns, computeTimePositions, groupEventsByDate,} from "trud-calendar-core";Recurrence
Section titled “Recurrence”RFC 5545 RRULE expansion engine:
import { expandRecurringEvents, generateOccurrences,} from "trud-calendar-core";
// Expand all recurring events in a date rangeconst expanded = expandRecurringEvents(events, "2026-03-01", "2026-03-31");
// Generate occurrence dates for a single ruleconst dates = generateOccurrences( { freq: "weekly", byDay: ["MO", "WE", "FR"] }, "2026-03-01", // start date "2026-03-01", // range start "2026-03-31", // range end);See Recurrence for detailed documentation.
Undo/Redo
Section titled “Undo/Redo”A generic, framework-agnostic undo stack:
import { createUndoStack, pushState, undo, redo, canUndo, canRedo,} from "trud-calendar-core";import type { UndoStack } from "trud-calendar-core";
let stack = createUndoStack(initialEvents); // UndoStack<CalendarEvent[]>stack = pushState(stack, newEvents); // snapshot current statestack = undo(stack); // go backstack = redo(stack); // go forwardcanUndo(stack); // booleancanRedo(stack); // booleanThe stack is immutable — each operation returns a new UndoStack. Default max history is 30 states.
Virtualization
Section titled “Virtualization”Viewport-based event filtering for large datasets:
import { filterVisibleEvents, scrollToViewportRange,} from "trud-calendar-core";import type { VirtualRange } from "trud-calendar-core";
// Filter PositionedEvent[] to only those visible in the viewportconst visible = filterVisibleEvents(positioned, viewportTop, viewportBottom, overscan);
// Convert scroll position to hour rangeconst range: VirtualRange = scrollToViewportRange( scrollTop, // current scroll offset containerHeight, // visible container height totalHeight, // total scrollable height dayStartHour, // e.g., 0 dayEndHour, // e.g., 24);// range.startHour, range.endHourState management
Section titled “State management”A reducer for calendar navigation state:
import { calendarReducer, createInitialState,} from "trud-calendar-core";
const state = createInitialState("2026-03-13", "month");// { currentDate: "2026-03-13", view: "month" }
const next = calendarReducer(state, { type: "NAVIGATE_NEXT" });// { currentDate: "2026-04-13", view: "month" }Actions: NAVIGATE_PREV, NAVIGATE_NEXT, NAVIGATE_TODAY, SET_DATE, SET_VIEW.
Constants
Section titled “Constants”import { DEFAULT_LABELS, DEFAULT_LOCALE, DEFAULT_VIEW, VIEWS, // ["month", "week", "day", "agenda"] DEFAULT_DAY_START_HOUR, // 0 DEFAULT_DAY_END_HOUR, // 24 HOURS_IN_DAY, // 24 MINUTES_IN_HOUR, // 60 MINUTES_IN_DAY, // 1440} from "trud-calendar-core";Example: Build a calendar in vanilla JS
Section titled “Example: Build a calendar in vanilla JS”import { getVisibleRange, filterEventsInRange, computeTimePositions, eachDayOfRange, formatTime, formatWeekdayShort,} from "trud-calendar-core";
// Get the visible date range for a week view starting Mondayconst range = getVisibleRange("2026-03-13", "week", 1);const days = eachDayOfRange(range.start, range.end);
// Filter eventsconst visible = filterEventsInRange(myEvents, range.start, range.end);
// Compute time-grid positions for a dayconst dayEvents = visible.filter(e => e.start.startsWith("2026-03-13"));const positioned = computeTimePositions(dayEvents, 8, 20);
// Renderfor (const { event, top, height, column, totalColumns } of positioned) { const el = document.createElement("div"); el.textContent = `${formatTime(event.start, "en-US")} ${event.title}`; el.style.position = "absolute"; el.style.top = `${top}%`; el.style.height = `${height}%`; el.style.left = `${(column / totalColumns) * 100}%`; el.style.width = `${100 / totalColumns}%`; container.appendChild(el);}