React Hooks
useCalendar, useSchedule, and useTimeline are the hook-shaped alternative to managing your own useRef. Each one returns:
ref— a callback ref to spread on the component (<Schedule ref={ref} />).instance— a reactive state value.nullbefore mount, the lemonadejs instance after. React re-renders when it transitions, so you can drive UI off of "is the component ready?".
The plain useRef pattern is still supported — pick whichever fits your component shape better.
Why a hook over a ref?
Section titled “Why a hook over a ref?”A manual useRef works, but its .current is not reactive — React doesn't re-render when it's assigned. The hook wraps that handshake in useState so you can do things like:
- disable buttons until the component has mounted
- conditionally render toolbars based on instance state
- capture the instance for sibling components without forwarding refs
import { Schedule, useSchedule } from '@calendarjs/react';
export default function App() {
const { ref, instance } = useSchedule();
return (
<>
<Toolbar disabled={!instance} onAdd={() => instance.addEvents([{ start: '09:00' }])} />
<Schedule ref={ref} />
</>
);
}
Schedule
Section titled “Schedule”import { useState } from 'react';
import { Schedule, useSchedule } from '@calendarjs/react';
export default function ScheduleApp() {
const { ref, instance } = useSchedule();
const [events, setEvents] = useState([]);
const addQuickEvent = () => {
instance.addEvents([
{ start: '09:00', end: '10:00', date: '2025-01-15', title: 'Quick' },
]);
};
return (
<>
<button disabled={!instance} onClick={addQuickEvent}>
Add quick event
</button>
<button disabled={!instance} onClick={() => instance.undo()}>
Undo
</button>
<button disabled={!instance} onClick={() => instance.redo()}>
Redo
</button>
<Schedule
ref={ref}
type="week"
value="2025-01-15"
data={events}
onCreate={(created) => setEvents((prev) => [...prev, ...created])}
style={{ height: 500 }}
/>
</>
);
}
Calendar
Section titled “Calendar”import { useState } from 'react';
import { Calendar, useCalendar } from '@calendarjs/react';
export default function CalendarApp() {
const { ref, instance } = useCalendar();
const [date, setDate] = useState('2025-01-15');
return (
<>
<div style={{ display: 'flex', gap: 8 }}>
<button disabled={!instance} onClick={() => instance.prev()}>Prev</button>
<button disabled={!instance} onClick={() => instance.setValue(new Date())}>Today</button>
<button disabled={!instance} onClick={() => instance.next()}>Next</button>
</div>
<Calendar ref={ref} type="inline" value={date} onChange={setDate} />
</>
);
}
Timeline
Section titled “Timeline”import { useState } from 'react';
import { Timeline, useTimeline } from '@calendarjs/react';
export default function TimelineApp() {
const { ref, instance } = useTimeline();
const [data, setData] = useState([
{ date: '2025-01-01', title: 'Project Started' },
{ date: '2025-02-01', title: 'Milestone 1' },
]);
return (
<>
<div>
<button disabled={!instance} onClick={() => instance.prev()}>Previous month</button>
<button disabled={!instance} onClick={() => instance.next()}>Next month</button>
</div>
<Timeline ref={ref} type="monthly" value="2025-01-01" data={data} />
</>
);
}
TypeScript
Section titled “TypeScript”The hooks are typed against the corresponding Instance type from each upstream namespace:
import { useSchedule } from '@calendarjs/react';
import type { Schedule as ScheduleNS } from '@calendarjs/react';
export default function App() {
// instance is `ScheduleNS.Instance | null`
const { ref, instance } = useSchedule();
if (instance) {
// Fully typed — autocompletes addEvents, getData, undo, etc.
instance.addEvents([{ start: '09:00' }]);
}
return <ScheduleNS /* ... */>{null}</ScheduleNS>;
}
Composing your own hook
Section titled “Composing your own hook”Wrap the built-in hook for app-specific behaviour — autosave, default options, instrumented actions:
import { useCallback, useEffect } from 'react';
import { useSchedule } from '@calendarjs/react';
export function useAutosaveSchedule(persist) {
const { ref, instance } = useSchedule();
useEffect(() => {
if (!instance) return;
const handler = () => persist(instance.getData());
const id = setInterval(handler, 30_000);
return () => clearInterval(id);
}, [instance, persist]);
const addAndSave = useCallback(
(events) => {
const created = instance?.addEvents(events);
if (created) persist(instance.getData());
return created;
},
[instance, persist],
);
return { ref, instance, addAndSave };
}
When to use a plain useRef instead
Section titled “When to use a plain useRef instead”The hooks return a callback ref — perfect when you want a reactive instance. If you don't need re-renders driven by mount state, plain useRef is one less re-render and one less import:
import { useRef } from 'react';
import { Schedule } from '@calendarjs/react';
export default function App() {
const ref = useRef();
return <Schedule ref={ref} data={[]} />;
}
Both patterns are fully supported and can be mixed in the same app.
Related
Section titled “Related”- Getting Started — first steps with
@calendarjs/react - TypeScript — input vs resolved types, typed refs
- Schedule / Calendar / Timeline — per-component docs