Headless timeline library for React.
One data model, multiple views — Schedule, Calendar, Grid across Day, Week, and Month.
- Headless core + ready-to-use UI — Use pre-built components or build your own with hooks
- Multiple views — Schedule (resource × time), Calendar, Grid × Day/Week/Month
- Large datasets — Virtual scrolling for 10,000+ events at 60fps
- Customizable — CSS variables for theming, dark mode, custom renderers
- TypeScript — Fully typed API
- MIT licensed — Free for commercial use
| Package | Description |
|---|---|
@chronoview/core |
Framework-agnostic core engine (time calculations, layout, stacking) |
@chronoview/react |
React hooks and context (headless) |
@chronoview/ui |
Pre-built UI components with design tokens |
npm install @chronoview/uiOther package managers
pnpm add @chronoview/ui
yarn add @chronoview/ui
bun add @chronoview/ui@chronoview/ui includes @chronoview/core and @chronoview/react as dependencies.
Peer dependencies: react >= 18, react-dom >= 18, @floating-ui/react >= 0.27
Import the stylesheet in your app's entry CSS file or root component:
@import "@chronoview/ui/styles.css";This single file includes all design tokens and component styles. No Tailwind CSS required in your project.
import { Schedule } from "@chronoview/ui";
import type { Resource, TimelineEvent } from "@chronoview/core";
const resources: Resource[] = [
{ id: "room-1", title: "Meeting Room A", color: "#3b82f6" },
{ id: "room-2", title: "Meeting Room B", color: "#8b5cf6" },
];
const events: TimelineEvent[] = [
{
id: "evt-1",
resourceId: "room-1",
start: new Date(2026, 2, 29, 9, 0),
end: new Date(2026, 2, 29, 10, 30),
title: "Team Standup",
},
{
id: "evt-2",
resourceId: "room-1",
start: new Date(2026, 2, 29, 14, 0),
end: new Date(2026, 2, 29, 15, 30),
title: "Client Meeting",
},
{
id: "evt-3",
resourceId: "room-2",
start: new Date(2026, 2, 29, 10, 0),
end: new Date(2026, 2, 29, 11, 30),
title: "Design Review",
color: "#10b981",
},
];
function App() {
return <Schedule events={events} resources={resources} />;
}This renders a day schedule with a toolbar (date navigation + view toggle), resource sidebar, time header, event cards, and a now indicator — all with sensible defaults.
Switch between Day, Week, and Month views:
<Schedule
events={events}
resources={resources}
view="week"
startDate={new Date(2026, 2, 23)}
/>Calendar displays events in a vertical time axis (like Google Calendar). Resources are used for color and filtering only — there are no resource rows.
import { Calendar } from "@chronoview/ui";
import type { Resource, TimelineEvent } from "@chronoview/core";
const resources: Resource[] = [
{ id: "work", title: "Work", color: "#3b82f6" },
{ id: "personal", title: "Personal", color: "#10b981" },
];
const events: TimelineEvent[] = [
{
id: "evt-1",
resourceId: "work",
start: new Date(2026, 2, 29, 9, 0),
end: new Date(2026, 2, 29, 10, 30),
title: "Team Standup",
},
{
id: "evt-2",
resourceId: "personal",
start: new Date(2026, 2, 29, 10, 0),
end: new Date(2026, 2, 29, 11, 0),
title: "Dentist",
color: "#8b5cf6",
},
];
function App() {
return <Calendar events={events} resources={resources} />;
}This renders a day calendar with a vertical time axis, overlapping event stacking, toolbar, and now indicator — auto-scrolled to the current time.
<Calendar events={events} resources={resources} view="week" />
<Calendar events={events} resources={resources} view="month" />Calendar Month supports two display modes:
// Horizontal event bars (default)
<Calendar events={events} resources={resources} view="month" monthMode="bar" />
// List inside each cell
<Calendar events={events} resources={resources} view="month" monthMode="list" />Calendar text defaults to Korean. Override with the labels prop:
<Calendar
events={events}
resources={resources}
labels={{
weekdays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
empty: "No events",
moreEvents: (count) => `${count} more`,
}}
/>Both Schedule and Calendar follow the system preference (prefers-color-scheme) automatically. No extra setup needed.
To force a specific theme, use the theme prop:
<Schedule events={events} resources={resources} theme="dark" />
<Calendar events={events} resources={resources} theme="dark" />Class-based (dark, [data-theme="dark"]) selectors are also supported for integration with theme libraries like next-themes.
Hover an event to see a built-in tooltip. Click an event to show a custom popover. Works the same way for both Schedule and Calendar:
<Schedule
events={events}
resources={resources}
renderEventDetail={(event, { close }) => (
<div>
<h3>{event.title}</h3>
<p>{event.data?.description}</p>
<button onClick={close}>Close</button>
</div>
)}
/>To disable the hover tooltip while keeping the popover, add disableTooltip:
<Schedule
events={events}
resources={resources}
disableTooltip
renderEventDetail={(event, { close }) => (
<div>
<h3>{event.title}</h3>
<button onClick={close}>Close</button>
</div>
)}
/>By default, Schedule and Calendar manage date state internally. To control it externally, pass date and onDateChange:
import { useState } from "react";
const [date, setDate] = useState(new Date());
<Schedule
events={events}
resources={resources}
date={date}
onDateChange={setDate}
/>Override CSS variables to match your design system:
:root {
--cv-color-event-default: #ff6b00;
--cv-color-bg: #fafafa;
--cv-size-sidebar-width: 240px;
}All available tokens are listed in docs/design/common/design-tokens.md.
Use @chronoview/react hooks directly for full control over rendering:
pnpm add @chronoview/react @chronoview/coreimport { useScheduleView } from "@chronoview/react";
import type { Resource, TimelineEvent } from "@chronoview/core";
function CustomSchedule({ events, resources }: {
events: TimelineEvent[];
resources: Resource[];
}) {
const { rows, getEventStyle } = useScheduleView({
events,
resources,
view: "day",
startDate: new Date(),
});
return (
<div style={{ position: "relative" }}>
{rows.map((row) => (
<div key={row.resource.id} style={{ position: "relative", height: row.height }}>
<span>{row.resource.title}</span>
{row.events.map((layout) => (
<div key={layout.event.id} style={getEventStyle(layout)}>
{layout.event.title}
</div>
))}
</div>
))}
</div>
);
}import { useCalendarView } from "@chronoview/react";
import type { Resource, TimelineEvent } from "@chronoview/core";
function CustomCalendar({ events, resources }: {
events: TimelineEvent[];
resources: Resource[];
}) {
const { columns, totalMainSize, getEventStyle } = useCalendarView({
events,
resources,
view: "day",
startDate: new Date(),
});
return (
<div style={{ position: "relative", height: totalMainSize }}>
{columns[0]?.events.map((layout) => (
<div key={layout.event.id} style={getEventStyle(layout, 0, 1)}>
{layout.event.title}
</div>
))}
</div>
);
}0.2.0 — Schedule and Calendar layouts (Day/Week/Month) are available. Grid layout is coming in the next release.
- Schedule Day/Week/Month: available
- Calendar Day/Week/Month: available
- Grid Day: planned (0.3.0)
- Drag & Drop, Resize: planned (0.4.0)
- Optimized for desktop (1024px+). Responsive support is planned for a future release.