Skip to content

Hooks

All hooks must be used inside a <CalendarProvider> component. If you’re using the <Calendar> component, the provider is already set up for you.

Main orchestrator — combines state, events, and navigation.

import { useCalendar } from "trud-calendar";
const {
currentDate, // DateString — current focused date
view, // CalendarView — current view
events, // CalendarEvent[] — all events
visibleEvents, // CalendarEvent[] — filtered to visible range
visibleRange, // { start: DateString, end: DateString }
locale, // string — BCP 47 locale
prev, // () => void — go to previous period
next, // () => void — go to next period
today, // () => void — go to today
setDate, // (date: DateString) => void
setView, // (view: CalendarView) => void
} = useCalendar();

Date navigation and view switching. Lighter than useCalendar — use this when you only need nav controls.

import { useNavigation } from "trud-calendar";
const {
currentDate, // DateString
view, // CalendarView
formattedDate, // string — locale-aware title (e.g., "March 2026")
prev, // () => void
next, // () => void
today, // () => void
setDate, // (date: DateString) => void
setView, // (view: CalendarView) => void
} = useNavigation();

Access event data for the current visible range.

import { useEvents } from "trud-calendar";
const {
visibleEvents, // CalendarEvent[] — sorted, filtered to visible range
getForDay, // (date: DateString) => CalendarEvent[]
partitioned, // { allDay: CalendarEvent[], timed: CalendarEvent[] }
segments, // EventSegment[] — multi-day event segments
groupedByDate, // Map<DateString, CalendarEvent[]>
} = useEvents();
const { getForDay } = useEvents();
const todayEvents = getForDay("2026-03-13");

Column-packing algorithm for overlapping events. Used internally by the week/day time grid — call it yourself when building custom time-based layouts.

import { useEventLayout } from "trud-calendar";
const positioned: PositionedEvent[] = useEventLayout(timedEvents);

Each PositionedEvent contains:

interface PositionedEvent {
event: CalendarEvent;
column: number; // 0-based column index in overlap group
totalColumns: number; // Total columns in overlap group
top: number; // % from top (0-100)
height: number; // % height (0-100)
}

Use these values to position events with CSS:

{positioned.map(({ event, column, totalColumns, top, height }) => (
<div
key={event.id}
style={{
position: "absolute",
top: `${top}%`,
height: `${height}%`,
left: `${(column / totalColumns) * 100}%`,
width: `${100 / totalColumns}%`,
}}
>
{event.title}
</div>
))}

Live clock for “current time” indicators. Updates every 60 seconds by default.

import { useCurrentTime } from "trud-calendar";
const {
now, // string — ISO datetime of current moment
today, // string — YYYY-MM-DD of today
timeOfDay, // number — fractional hour (14.5 = 2:30 PM)
} = useCurrentTime();
// Custom interval: update every 10 seconds
const time = useCurrentTime(10_000);
const { timeOfDay } = useCurrentTime();
const topPercent = ((timeOfDay - dayStartHour) / (dayEndHour - dayStartHour)) * 100;
<div
className="absolute left-0 right-0 border-t-2 border-red-500 z-10"
style={{ top: `${topPercent}%` }}
/>

Locale-aware date formatting functions powered by the Intl API.

import { useDateFormat } from "trud-calendar";
const fmt = useDateFormat();
fmt.toolbarTitle("2026-03-13", "month"); // "March 2026"
fmt.toolbarTitle("2026-03-13", "week"); // "Mar 9 – 15, 2026"
fmt.time("2026-03-13T14:30:00"); // "2:30 PM"
fmt.timeRange(start, end); // "2:30 – 3:00 PM"
fmt.weekdayShort("2026-03-13"); // "Fri"
fmt.weekdayNarrow("2026-03-13"); // "F"
fmt.dayNumber("2026-03-13"); // "13"
fmt.agendaDate("2026-03-13"); // "Friday, March 13"
fmt.monthDay("2026-03-13"); // "Mar 13"

All functions respect the locale set in the CalendarProvider.

Pointer-based event drag hook for building custom drag interactions. Used internally by WeekView and MonthView.

import { useEventDrag } from "trud-calendar";
const {
dragState, // DragState | null — current drag state
onPointerDown, // (e: React.PointerEvent, event: CalendarEvent) => void
isDragging, // boolean
didDrag, // React.RefObject<boolean> — true if a drag occurred (suppress click)
} = useEventDrag({
mode: "time", // "time" for week/day, "date" for month
onEventDrop, // (event, newStart, newEnd) => void
enableDnD, // boolean
});

DragState contains:

  • event — the event being dragged
  • ghostX, ghostY — cursor position for rendering a drag ghost
  • targetDay — the day column the cursor is over (detected via data-day attributes)

Supports both mouse and touch interactions via Pointer Events API.

Keyboard navigation hook implementing the WAI-ARIA roving tabindex grid pattern.

import { useGridKeyboard } from "trud-calendar";
const grid = useGridKeyboard({
cols: 7,
rows: 24,
onActivate: (row, col) => { /* Enter/Space pressed */ },
onEscape: () => { /* Escape pressed */ },
});

Returns:

  • registerCell(row, col) — ref callback for grid cells
  • getTabIndex(row, col) — returns 0 for the active cell, -1 for others
  • handleKeyDown — keyboard event handler
  • handleFocus(row, col) — focus event handler

Wraps a CalendarEvent[] with a full undo/redo stack. Auto-snapshots before onEventDrop and onEventResize apply, and registers Ctrl+Z / Ctrl+Shift+Z (Ctrl+Y) keyboard shortcuts.

import { useUndoableEvents } from "trud-calendar";
const {
events, // CalendarEvent[] — current state
setEvents, // (events: CalendarEvent[]) => void — update + auto-snapshot
onEventDrop, // (event, newStart, newEnd) => void — snapshots before applying
onEventResize, // (event, newStart, newEnd) => void — snapshots before applying
undo, // () => void
redo, // () => void
canUndo, // boolean
canRedo, // boolean
snapshot, // () => void — manual snapshot (e.g., before a batch update)
} = useUndoableEvents({
initialEvents: myEvents,
maxHistory: 30, // optional, defaults to 30
});
function App() {
const undoable = useUndoableEvents({ initialEvents: myEvents });
return (
<>
<div className="flex gap-2 mb-2">
<button onClick={undoable.undo} disabled={!undoable.canUndo}>Undo</button>
<button onClick={undoable.redo} disabled={!undoable.canRedo}>Redo</button>
</div>
<Calendar
events={undoable.events}
enableDnD
onEventDrop={undoable.onEventDrop}
onEventResize={undoable.onEventResize}
/>
</>
);
}

Multi-select event management with Set-based tracking. Supports toggle (Ctrl+click), range select (Shift+click), select all, and clear.

import { useEventSelection } from "trud-calendar";
const {
selectedIds, // Set<string> — currently selected event IDs
isSelected, // (id: string) => boolean
select, // (id: string) => void — single select (clears others)
toggle, // (id: string) => void — Ctrl+click behavior
rangeSelect, // (id: string, sortedIds: string[]) => void — Shift+click
clearSelection, // () => void
selectAll, // (ids: string[]) => void
lastSelectedId, // string | null — anchor for range selection
} = useEventSelection();

The <Calendar> component uses SelectionProvider internally, which wraps useEventSelection with modifier key detection. Selected events show a visual ring highlight.

Adaptive layout hook using ResizeObserver. Returns breakpoint flags and the recommended number of visible day columns.

import { useResponsiveView } from "trud-calendar";
const ref = useRef<HTMLDivElement>(null);
const {
isMobile, // boolean — container < 640px
isTablet, // boolean — container 640px–1024px
containerWidth, // number — measured width in px
visibleDays, // 1 (mobile) | 3 (tablet) | 7 (desktop)
} = useResponsiveView(ref);

The <Calendar> component uses this internally to adapt the week view column count and month view cell sizes.

Touch swipe detection for prev/next navigation. Only activates for pointerType === "touch" — mouse drags are ignored.

import { useSwipeNavigation } from "trud-calendar";
const swipe = useSwipeNavigation({
onSwipeLeft: () => next(), // navigate forward
onSwipeRight: () => prev(), // navigate backward
threshold: 50, // min horizontal px (default: 50)
maxVertical: 30, // max vertical px (default: 30)
});
// Spread on the container element:
<div
onPointerDown={swipe.onPointerDown}
onPointerMove={swipe.onPointerMove}
onPointerUp={swipe.onPointerUp}
>
{/* calendar content */}
</div>

Viewport-based scroll tracking for filtering visible events. Useful for large datasets where rendering all events is expensive.

import { useVirtualScroll } from "trud-calendar";
const {
viewportTop, // number — top edge as % (0–100)
viewportBottom, // number — bottom edge as % (0–100)
isVirtualized, // boolean — true when enabled
} = useVirtualScroll({
containerRef: scrollRef,
totalHours: 24,
enabled: true, // default: false
overscanHours: 2, // extra hours to render above/below viewport
});

Pair with filterVisibleEvents() from trud-calendar-core to cull off-screen events:

import { filterVisibleEvents } from "trud-calendar-core";
const visible = filterVisibleEvents(positioned, viewportTop, viewportBottom);