Skip to content

TypeScript

@calendarjs/react ships first-class TypeScript types — no @types/* install needed.

The package distinguishes two type shapes per component:

  • Input shape — Schedule.EventInput, Timeline.ItemInput. Loose: only the truly required field is mandatory; everything else is optional. The component fills in defaults (guid, title, color, type, ...) on render. Includes an index signature so you can attach app metadata without casts.
  • Resolved shape — Schedule.Event, Timeline.Item. Strict: what getData() returns and what callbacks like onCreate / onChangeEvent hand back, with all defaults filled in.

This split is the same pattern React uses for CSSProperties and Next.js uses for route handler params: user provides loose, framework hands back strict.

import { Schedule, Timeline, Calendar } from '@calendarjs/react';
import type {
    Schedule as ScheduleNS,
    Timeline as TimelineNS,
    Calendar as CalendarNS,
} from '@calendarjs/react';

Each *NS import gives you access to the namespace members (Event, EventInput, Options, Instance, etc.) under the same name. The runtime imports (Schedule, Timeline, Calendar) remain the React component values.

import type { Schedule as ScheduleNS } from '@calendarjs/react';

// ✅ Loose input — only `start` is required.
const minimal: ScheduleNS.EventInput = { start: '09:00' };

// ✅ App-specific metadata is allowed via index signature, no `as any`.
const enriched: ScheduleNS.EventInput = {
    start: '09:00',
    end: '10:00',
    class_id: 42,
    teacher_id: 7,
};

// `Event` (resolved) requires guid/title/color/type — typically you receive
// these from callbacks rather than constructing them yourself.
function logEvent(ev: ScheduleNS.Event) {
    console.log(ev.guid, ev.title, ev.color);
}
import type { Timeline as TimelineNS } from '@calendarjs/react';

// ✅ Only `date` is required on input.
const items: TimelineNS.ItemInput[] = [
    { date: '2025-01-15' },
    { date: '2025-02-01', title: 'Milestone 1' },
    { date: '2025-03-01', id: 42, status: 'done' }, // app metadata is fine
];

Use useRef<NS.Instance>(null) instead of useRef<any> to get full method autocomplete:

import { useRef } from 'react';
import { Schedule } from '@calendarjs/react';
import type { Schedule as ScheduleNS } from '@calendarjs/react';

export default function App() {
    const ref = useRef<ScheduleNS.Instance>(null);

    const addOne = () => {
        // Fully typed — autocompletes addEvents, getData, undo, etc.
        ref.current?.addEvents([{ start: '09:00', end: '10:00' }]);
    };

    return <Schedule ref={ref} data={[]} />;
}

The hooks (useSchedule, useTimeline, useCalendar) are pre-typed — no annotation needed:

import { useSchedule } from '@calendarjs/react';

const { ref, instance } = useSchedule();
//      ^ (instance: ScheduleNS.Instance | null) => void
//                ^ ScheduleNS.Instance | null

onCreate and onBeforeCreate always receive an array (length ≥ 1) — no union, no Array.isArray unwrap:

import { Schedule } from '@calendarjs/react';
import type { Schedule as ScheduleNS } from '@calendarjs/react';

const onCreate = (events: ScheduleNS.Event[]) => {
    events.forEach((ev) => console.log(ev.guid));
};

const onBeforeCreate = (events: ScheduleNS.Event[]): boolean | void => {
    if (events.some((ev) => ev.start < '09:00')) return false; // cancel
};

<Schedule onCreate={onCreate} onBeforeCreate={onBeforeCreate} data={[]} />

Per-event callbacks (onEdition, onDblClick, onDelete, onChangeEvent) receive a single resolved Event:

const onChangeEvent = (
    event: ScheduleNS.Event,
    oldValue: Partial<ScheduleNS.Event>,
    newValue: Partial<ScheduleNS.Event>,
) => {
    console.log(`Event ${event.guid}: changed from`, oldValue, 'to', newValue);
};

The instance methods accept loose input types (so consumers can pass app metadata) and return resolved types:

import type { Schedule as ScheduleNS } from '@calendarjs/react';

const ref = useRef<ScheduleNS.Instance>(null);

// addEvents accepts EventInput | EventInput[] (loose), returns Event[] (resolved).
const created: ScheduleNS.Event[] | false | undefined =
    ref.current?.addEvents([{ start: '09:00', class_id: 42 }]);

// updateEvent accepts Partial<EventInput> for `newValue`.
ref.current?.updateEvent('guid-1', { title: 'New title', class_id: 99 });

// setData accepts EventInput[].
ref.current?.setData([{ start: '09:00', class_id: 42 }]);

// getData returns Event[] (resolved).
const all: ScheduleNS.Event[] | undefined = ref.current?.getData();

The index signature on EventInput accepts any keys, but loses type safety for those keys. To keep your app metadata fully typed, intersect with a domain interface:

import type { Schedule as ScheduleNS } from '@calendarjs/react';

interface ClassMeta {
    class_id: number;
    teacher_id: number;
    room: string;
}

type ClassEvent = ScheduleNS.EventInput & ClassMeta;

const events: ClassEvent[] = [
    { start: '09:00', end: '10:00', class_id: 42, teacher_id: 7, room: 'A1' },
    // missing class_id would now be a TS error
];

// On the way out, intersect Event with the same metadata.
const onCreate = (created: Array<ScheduleNS.Event & ClassMeta>) => {
    created.forEach((ev) => console.log(ev.guid, ev.class_id));
};

This snippet should compile under strict TS with no casts:

import { useState } from 'react';
import { Schedule, Timeline } from '@calendarjs/react';
import type { Schedule as ScheduleNS } from '@calendarjs/react';

export default function App() {
    // Loose input — extra app fields, no cast.
    const [events, setEvents] = useState<ScheduleNS.EventInput[]>([
        { start: '09:00', end: '10:00', class_id: 42, teacher_id: 7 },
        { start: '10:00', end: '11:00', class_id: 42, teacher_id: 7 },
    ]);

    // Always Event[], no union.
    const onCreate = (created: ScheduleNS.Event[]) => {
        setEvents((prev) => [...prev, ...created]);
    };

    // Timeline items with extra `id` field — no cast.
    const items = [
        { id: 1, title: 'A', date: '2026-01-01' },
        { id: 2, title: 'B', date: '2026-01-02' },
    ];

    return (
        <>
            <Schedule data={events} onCreate={onCreate} />
            <Timeline data={items} order="asc" />
        </>
    );
}