Skip to content

Data Management

Programmatically add, update, and remove timeline entries using React state.

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

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

    const addEntry = () => {
        setData([...data, {
            title: 'New Entry',
            date: '2025-02-01',
            borderColor: '#e74c3c'
        }]);
    };

    const updateFirst = () => {
        if (data.length > 0) {
            const updated = [...data];
            updated[0] = { ...updated[0], title: 'Updated Entry', borderColor: '#2ecc71' };
            setData(updated);
        }
    };

    const removeLast = () => {
        if (data.length > 0) {
            setData(data.slice(0, -1));
        }
    };

    return (
        <div>
            <button onClick={addEntry}>Add</button>
            <button onClick={updateFirst}>Update</button>
            <button onClick={removeLast}>Remove</button>
            <Timeline data={data} align="left" />
        </div>
    );
}

The wrapper diff-checks props with !==. Replacing the array (setData([...data, x])) re-renders; mutating in place (data.push(x)) does not — the reference hasn't changed. Always return a new array from your state updater.

Items accept arbitrary keys via the ItemInput index signature — useful for joining timeline data to your domain model without casts:

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

export default function App() {
    const [data, setData] = useState([
        { id: 101, title: 'Sprint 1', date: '2025-01-15', status: 'done' },
        { id: 102, title: 'Sprint 2', date: '2025-02-15', status: 'in-progress' },
    ]);

    return (
        <Timeline
            data={data}
            onEdition={(entry) => console.log('Edit', entry.id, entry.status)}
        />
    );
}