historical-timeline-react
v0.1.0
Published
Private React vertical timeline component with discrete zoom from `century` down to `day`.
Readme
historical-timeline-react
Private React vertical timeline component with discrete zoom from century down to day.
This repo currently contains:
- the reusable timeline library
- a local demo app used for development and iteration
Related repo:
- consumer example app:
https://github.com/yehaisong/timeline-react-consumer
Status
The library surface is being extracted for private publishing through GitHub Packages.
Install
npm install historical-timeline-reactUsage
Import the library stylesheet once:
import 'historical-timeline-react/styles.css';Minimal usage:
import {
VerticalTimeline,
type TimelineEvent,
} from 'historical-timeline-react';
const events: TimelineEvent[] = [
{
id: 'moon-landing',
title: 'Apollo 11 Moon Landing',
start: '1969-07-20',
importance: 10,
},
];
export function Example() {
return (
<VerticalTimeline
events={events}
maxZoomUnit="century"
minZoomUnit="day"
initialZoomUnit="decade"
height={800}
unitHeight={100}
/>
);
}Event Date Fields
The component supports three separate date concerns:
start/end: semantic source-of-truth event datesplacementStart/placementEnd: optional exact dates used only for placement on the timelinedisplayStart/displayEnd: optional strings used only for built-in date text in the detail panel and cluster list
This is useful when your source data is year-only, but you still want more precise placement on the rail.
Example:
const events: TimelineEvent[] = [
{
id: 'azusa-street',
title: 'Azusa Street Revival',
start: '1906',
end: '1909',
placementStart: '1906-04-09',
placementEnd: '1909-01-01',
displayStart: '1906',
displayEnd: '1909',
},
];Demo Settings Template
The demo app loads its timeline behavior and appearance from:
src/data/timeline-settings.json
Sample alternate templates are included at:
src/data/timeline-settings-ocean.jsonsrc/data/timeline-settings-ember.json
That JSON file is the template for:
- zoom bounds
- default zoom
- unit height
- detail mode
- display options
- theme options
The settings panel edits that same shape in memory, and Reset restores the values from the JSON file.
Public Config Surface
Behavior options:
type TimelineDisplayOptions = {
showMiniMap?: boolean;
showMajorTicks?: boolean;
showMajorLabels?: boolean;
showMinorTicks?: boolean;
showMinorLabels?: boolean;
miniMapWidth?: number;
clusterLaneLimit?: number;
};Theme options:
type TimelineTheme = {
axisColor?: string;
majorTickColor?: string;
minorTickColor?: string;
labelPillBg?: string;
labelPillText?: string;
eventCardBg?: string;
eventCardText?: string;
eventCardBorder?: string;
eventCardHoverBg?: string;
eventCardActiveBorder?: string;
miniMapBg?: string;
miniMapBorder?: string;
miniMapTrackColor?: string;
miniMapViewportBorder?: string;
miniMapViewportBg?: string;
miniMapDensityLow?: string;
miniMapDensityHigh?: string;
cardWidth?: number;
cardMaxWidth?: number;
stackOffset?: number;
axisWidth?: number;
axisOffset?: number;
labelShift?: number;
};Example:
<VerticalTimeline
events={events}
maxZoomUnit="century"
minZoomUnit="year"
initialZoomUnit="decade"
unitHeight={120}
display={{
showMiniMap: true,
showMajorTicks: true,
showMajorLabels: true,
showMinorTicks: false,
showMinorLabels: false,
miniMapWidth: 160,
}}
theme={{
axisColor: '#111827',
majorTickColor: '#374151',
minorTickColor: '#94a3b8',
labelPillBg: '#111827',
labelPillText: '#f8fafc',
cardMaxWidth: 240,
stackOffset: 96,
}}
/>Handling Event Clicks And Detail Panels
The timeline supports both patterns:
- consumer-managed detail UI through
onEventClick - built-in detail UI through
detailMode
Built-in detail options:
type TimelineDetailMode = 'none' | 'modal' | 'slide';The component default is slide.
Example:
<VerticalTimeline
events={events}
detailMode="slide"
/>If you want full control, use onEventClick to open your own detail UI, such as:
- a side drawer
- a modal
- a bottom sheet
- an inline detail panel
Consumer-managed example:
import { useState } from 'react';
import {
VerticalTimeline,
type NormalizedTimelineEvent,
type TimelineEvent,
} from 'historical-timeline-react';
const events: TimelineEvent[] = [
{
id: 'apollo-11',
title: 'Apollo 11 Moon Landing',
start: '1969-07-20',
description: 'First crewed lunar landing.',
},
];
export function TimelineWithDetailPanel() {
const [selectedEvent, setSelectedEvent] = useState<NormalizedTimelineEvent | null>(null);
return (
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) 320px', gap: 16 }}>
<VerticalTimeline
events={events}
maxZoomUnit="century"
minZoomUnit="day"
initialZoomUnit="decade"
height={800}
unitHeight={100}
onEventClick={setSelectedEvent}
/>
{selectedEvent ? (
<aside>
<h2>{selectedEvent.title}</h2>
<p>{selectedEvent.description}</p>
</aside>
) : null}
</div>
);
}Use the built-in detailMode when the default panel behavior is enough, and use onEventClick when your app needs a custom detail surface.
Scripts
npm run devruns the local demo appnpm run build:demobuilds the demo app todist-demonpm run buildnpm run build:libbuilds the library package todist
Current Scope
Library code:
src/componentssrc/hookssrc/libsrc/styles/library.csssrc/index.ts
Demo-only code:
src/App.tsxsrc/main.tsxsrc/styles/app.css- local sample data
Copyright
Copyright (c) 2026 Haisong Ye. Released under the MIT License.
Built with Codex GPT-5.4.
