@calendarjs/react
v1.0.8
Published
React wrappers for CalendarJS Calendar, Schedule, and Timeline. One install, named imports, idiomatic React API (camelCase callbacks, controlled components, ref forwarding).
Maintainers
Readme
@calendarjs/react
React wrappers for the CalendarJS family of components — Calendar, Schedule, and Timeline — published as a single package.
npm install @calendarjs/react reactUsage
import { Calendar, Schedule, Timeline } from '@calendarjs/react';Stylesheets are loaded automatically when you import the module, provided your bundler handles import '*.css' (Webpack, Vite, Next.js, CRA, etc.). If you need the consolidated stylesheet directly:
import '@calendarjs/react/style.css';Included components
Calendar
A date picker supporting single dates, ranges, time, validation, and inline / popup display.
import React, { useState } from 'react';
import { Calendar } from '@calendarjs/react';
export default function App() {
const [value, setValue] = useState('2025-01-15');
return <Calendar type="inline" value={value} onChange={setValue} />;
}Schedule
An event scheduler with day / week / weekdays views, drag-and-drop, conflict detection, and undo/redo.
import React, { useRef, useState } from 'react';
import { Schedule } from '@calendarjs/react';
export default function App() {
const ref = useRef();
const [events, setEvents] = useState([
{ title: 'Meeting', start: '09:00', end: '10:00', date: '2025-01-15', color: '#3498db' },
]);
return (
<Schedule
ref={ref}
type="week"
value="2025-01-15"
data={events}
onCreate={(events) => console.log('created', events)}
onChangeEvent={(event, oldValue, newValue) => console.log('updated', event)}
onDelete={(event) => console.log('deleted', event)}
/>
);
}Timeline
A chronological event display with monthly grouping, sorting, and custom alignment.
import React from 'react';
import { Timeline } from '@calendarjs/react';
export default function App() {
const data = [
{ title: 'Issue Identification', date: new Date(2025, 5, 1) },
{ title: 'Root Cause Analysis', date: new Date(2025, 5, 2) },
{ title: 'Implementation', date: new Date(2025, 5, 3), borderColor: '#808000', borderStyle: 'dashed' },
];
return <Timeline data={data} type="monthly" />;
}Idiomatic React API
All three components support:
- camelCase callbacks —
onChange,onCreate,onUpdate,onClose,onOpen,onDelete,onError,onChangeEvent,onBeforeChange,onBeforeCreate,onBeforeChangeEvent,onBeforeInsert,onEdition,onDblClick. The wrapper drops the leadingselfargument before invoking your handler — the underlying instance is available via the forwarded ref. refforwarding —<Calendar ref={r} />followed byr.current.next()etc.children— render overlays alongside the component.className/style— forwarded to the wrapper element.data-*/aria-*/role— passed through to the wrapper<div>(handy for Playwright / Testing Library).
<Schedule data-testid="weekly-schedule" aria-label="Class timetable" data={events} />Controlled vs imperative
Both modes are first-class. Pick by interaction style:
- Controlled — React state is the source of truth. Best for read-mostly views (timetable display, dashboards).
- Imperative — talk to the instance via the ref. Best for edit-heavy interaction where a full re-render would lose focus / drag selection (drag-to-create, in-place mutations).
// Controlled — replace the array reference to update.
const [events, setEvents] = useState([
{ start: '09:00', end: '10:00', date: '2025-01-15', title: 'Meeting' },
]);
return (
<Schedule
data={events}
onCreate={(created) => setEvents((prev) => [...prev, ...created])}
/>
);// Imperative — mutate via the ref, no React re-render.
const ref = useRef();
const flagWarning = (guid) => {
const ev = ref.current.data.find((e) => e.guid === guid);
ev.warning = true;
ref.current.render();
};
return <Schedule ref={ref} data={initialEvents} />;Reference identity matters
The wrapper diff-checks props with !==. Replace the array to trigger a re-render; mutating in place won't:
// ✅ New reference — re-renders
setEvents([...events, newEvent]);
// ❌ Same reference — no re-render
events.push(newEvent);If you need to edit events in place (toggle warning, adjust color, etc.), use the imperative ref pattern above and call ref.current.render().
Memoize handlers
The prop sync runs on every parent render. Inline arrow functions reallocate every time; lemonadejs's !== check absorbs the no-op, but useCallback makes the intent explicit and keeps the prop diff cheap:
const onCreate = useCallback(
(created) => setEvents((prev) => [...prev, ...created]),
[],
);
return <Schedule data={events} onCreate={onCreate} />;Hooks
useCalendar / useSchedule / useTimeline are an optional alternative to manual refs. Each returns a callback ref to pass to the component plus a reactive instance that is null before mount and the lemonadejs instance after — useful when you want to render conditionally based on whether the component is mounted.
import { Schedule, useSchedule } from '@calendarjs/react';
export default function App() {
const { ref, instance } = useSchedule();
const [events, setEvents] = useState([]);
return (
<>
<button disabled={!instance} onClick={() => instance.addEvents([{ start: '09:00' }])}>
Add event
</button>
<Schedule
ref={ref}
data={events}
onCreate={(created) => setEvents((prev) => [...prev, ...created])}
/>
</>
);
}The plain useRef pattern is still fully supported — pick whichever fits your component shape.
Migrating from @calendarjs/ce + a hand-rolled adapter
If your app currently wraps the vanilla CalendarJS bundle in a custom React adapter, you can drop in @calendarjs/react and delete the adapter:
| Before | After |
| --------------------------------------------------------- | ------------------------------------------------------ |
| import calendarjs from '@calendarjs/ce' | import { Schedule } from '@calendarjs/react' |
| Custom <ScheduleAdapter> around calendarjs.Schedule | Use <Schedule> directly |
| adapter.current.instance.addEvents(...) | ref.current.addEvents(...) (or useSchedule) |
| Hand-rolled prop diff in useEffect | Built in — pass props normally |
| Manual import 'calendarjs/dist/style.css' | Auto-loaded; or import '@calendarjs/react/style.css' |
| (self, events) => handler(events) callback wrapper | onCreate={(events) => ...} — no self argument |
| events as Schedule.Event[] cast on input data | Loose Schedule.EventInput[] accepted directly |
| Array.isArray(events) ? events : [events] in onCreate | events is always Schedule.Event[] (length ≥ 1) |
For per-component option, method, and event details, see:
Versioning
@calendarjs/react is a thin re-export package. It tracks the underlying plugins via semver-compatible peer ranges. To pin specific plugin versions, install them directly alongside this package.
License
MIT.
