Skip to content

React Timeline

2.1KB gzipped

The Timeline component is a lightweight React component for creating dynamic timelines in web applications. It supports event logs, roadmaps, and chronological displays with customizable styling. Includes automatic monthly grouping, sorting options, and positioning controls. Ships with @calendarjs/react alongside Calendar and Schedule.

  • Event logs and roadmaps
  • Monthly view with navigation
  • Ascending/descending sorting
  • Customizable positioning (left, right, top, bottom)
  • Border styles and colors
  • Responsive design
  • TypeScript support

CalendarJS is a complete calendar and scheduling solution that includes:

  • Calendar - Date picker with range selection (3.18KB)
  • Schedule - Event scheduler with drag & drop (4.2KB)
  • Timeline - Chronological event display (this page)

For interactive event scheduling with drag-and-drop instead of a chronological list, use the Schedule component:

  • Day, week, and weekdays views
  • Drag & drop event management
  • Resource scheduling
  • Time slot configuration
  • Undo/redo support

Perfect for: Appointment calendars, team schedules, booking systems, resource planning

Explore Schedule Component →

For date picking and selection before displaying timeline events, use the Calendar component:

  • Single and range date selection
  • Date validation
  • International formats
  • Keyboard navigation

Perfect for: Date range filters, report selectors, booking date pickers

Explore Calendar Component →

For reactive applications with data binding, LemonadeJS Timeline provides:

  • Reactive data binding
  • Vue, React, Angular integration
  • Built on CalendarJS Timeline (this component)
  • Framework-agnostic reactive wrapper

Explore LemonadeJS Timeline →


npm install @calendarjs/react react
PropertyDescription
data?: ItemInput[]Array of timeline items to display. Loose input shape — only date is required (see below).
type?: 'default' | 'monthly'Display mode. Monthly groups items by month with navigation. Default: 'default'.
align?: 'left' | 'right' | 'top' | 'bottom'Bullet point alignment. Default: 'left'.
message?: stringMessage shown when no data is available.
order?: 'asc' | 'desc'Sort order by date. Default: 'asc'.
width?: numberWidth of the timeline container in pixels.
height?: numberHeight of the timeline container in pixels.
value?: string | objectInitial value form type='monthly'.

The package distinguishes the input shape (what you pass into data) from the resolved shape (what callbacks like onUpdate hand back, with defaults filled in). See the TypeScript guide for full details.

Only date is required; everything else is optional. An index signature lets you attach app-specific metadata (id, foreign keys, status, ...) without casts.

PropertyDescription
date: string | DateRequired. Date associated with the item.
title?: stringTitle for the item.
subtitle?: stringSubtitle or secondary text.
description?: stringItem description or content.
borderColor?: stringBorder color (hex or CSS color).
borderStyle?: stringBorder style: 'solid', 'dashed', or 'dotted'.
editable?: booleanShow an edit icon on the item.
tags?: Tag[]Tags to display on this item (Tags →).
[key: string]: anyApp-specific metadata (no cast required).

Returned by result and passed to onUpdate. Extends ItemInput with title always present.

interface Item extends ItemInput {
    title: string;
}
EventDescription
onUpdateFired after result is recomputed (initial render + every change).
onUpdate?: (result: Item[]) => void
onEditionFired when the edit icon is clicked on an editable item.
onEdition?: (entry: Item) => void

The instance is available via the forwarded ref — your handler receives just the event data.

MethodDescription
next(): voidGo to the next month (in monthly type)
prev(): voidGo to the previous month (in monthly type)
import { Timeline } from '@calendarjs/react';

export default function App() {
    const data = [
        { title: 'Planning Phase', date: '2025-01-15', borderColor: '#3498db' },
        { title: 'Development', date: '2025-02-01', borderColor: '#e74c3c' },
        { title: 'Testing', date: '2025-02-15', borderColor: '#2ecc71' },
        { title: 'Deployment', date: '2025-03-01', borderColor: '#9b59b6' }
    ];

    return <Timeline data={data} align="left" />;
}
import { Timeline } from '@calendarjs/react';

export default function App() {
    const data = [
        { title: 'Sprint Planning', subtitle: 'Team A', date: '2025-01-05' },
        { title: 'Code Review', subtitle: 'Team B', date: '2025-01-12' },
        { title: 'Release v1.0', subtitle: 'Production', date: '2025-02-01' },
        { title: 'Sprint Retrospective', subtitle: 'Team A', date: '2025-02-15' }
    ];

    return <Timeline type="monthly" value="2025-01-05" data={data} style={{ width: 500, height: 400 }} />;
}

Replace the array reference (don't mutate in place) and the timeline re-renders:

import { useCallback, useState } from 'react';
import { Timeline } from '@calendarjs/react';

export default function App() {
    const [data, setData] = useState([
        { date: '2025-01-01', title: 'Project Started', borderColor: '#3498db' },
    ]);

    const addEntry = useCallback(() => {
        setData((prev) => [
            ...prev,
            { date: new Date().toISOString().split('T')[0], title: 'New Entry' },
        ]);
    }, []);

    return (
        <>
            <button onClick={addEntry}>Add entry</button>
            <Timeline data={data} align="left" />
        </>
    );
}

The hook gives you a callback ref plus a reactive instance — null before mount, the live instance after. Useful for monthly-view navigation buttons:

import { useState } from 'react';
import { Timeline, useTimeline } from '@calendarjs/react';

export default function App() {
    const { ref, instance } = useTimeline();
    const [data] = useState([
        { date: '2025-01-15', title: 'Sprint Planning' },
        { date: '2025-02-15', title: 'Sprint Retrospective' },
    ]);

    return (
        <>
            <button disabled={!instance} onClick={() => instance.prev()}>Previous month</button>
            <button disabled={!instance} onClick={() => instance.next()}>Next month</button>

            <Timeline ref={ref} type="monthly" value="2025-01-15" data={data} />
        </>
    );
}

The ItemInput index signature accepts arbitrary keys — no as any:

import { Timeline } from '@calendarjs/react';
import type { Timeline as TimelineNS } from '@calendarjs/react';

const items: TimelineNS.ItemInput[] = [
    { id: 1, title: 'A', date: '2026-01-01', status: 'done' },
    { id: 2, title: 'B', date: '2026-01-02', status: 'in-progress' },
];

<Timeline data={items} order="asc" />

See TypeScript for the full input-vs-resolved type guide.