react-calendar-timeline: a TypeScript rewrite still shipping as a beta
A modern and responsive react timeline component.
At a glance
- What is it?
- The long-standing React timeline component finally replaced moment with dayjs and webpack with Vite, but the README, the package version and the release feed have drifted apart.
- Who is it for?
- This is a component that works and a release process that has not quite caught up with it. The canvas timeline, the group sidebar and the drag and resize interactions are the substance of the library, and the beta line makes them usable on React 18 and 19 with bundled types and a much smaller date library.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 80 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A version number the README, package.json and releases disagree on
Three sources in this repository give three different pictures of what is current, and the disagreement is worth laying out because it changes what you install.
The README's announcement, near the top, says Version 0.30.0-beta.17 Now Available and lists what that beta includes: a full TypeScript rewrite with bundled type definitions, moment.js replaced with dayjs, Vite as bundler, React 18 and 19 support, dynamic item sizing, an itemVerticalGap prop, and a buffer prop for deciding when to redraw the canvas. The announcement also tells you the beta is on a dist tag:
npm install react-calendar-timeline@betaThe package.json in the repository root says version 0.30.0-beta.19, and the newest published release is v0.30.0-beta.19, dated the same day as the repository's last push. So the README is documenting a beta two versions behind the artifact npm will hand you when you install the tag.
What that means practically is that the README's feature list is a floor, not a ceiling. The beta.18 release in particular was a large piece of work the README does not mention at all: the entire test suite moved from Jest and Enzyme to Vitest and React Testing Library, all test files were converted to TypeScript, and placeholder tests were replaced with real ones across Columns, GroupRow, GroupRows, Items, Item, ScrollElement, Sidebar, Timeline, TimelineStateContext, markers, headers, PreventClickOnDrag and the utility functions. It also removed remaining any types, reformatted the codebase with Prettier through a lint-staged pre-commit hook, and moved ESLint to the legacy-recommended config.
The concrete check for anyone about to upgrade is to read the release notes for the beta number your lockfile has, not the announcement paragraph.
Migrating from the 0.2x stable line is a five item checklist
The README keeps a dedicated section for people on the stable line, and the first thing it says is that the latest stable release is 0.28.0, with a link to that version's own README. If you are not ready for the beta, that is the branch to install and that is the documentation to read:
npm install react-calendar-timelineThe stable version uses moment.js as its date library. Everything after that section documents the 0.30.0 beta API, which the README states plainly so you do not accidentally follow the wrong instructions.
The migration list itself is short and each item is a real code change rather than a config tweak. The date library changes from moment to dayjs, so your peer dependency and all date-related code need updating. The CSS import path moves from `react-calendar-timeline/lib/Timeline.css` to `react-calendar-timeline/style.css`, which is the kind of change that produces an unstyled timeline and a confused afternoon. React 16 and 17 are no longer supported and React 18 or later is required. ImmutableJS arrays are gone, so plain JavaScript arrays are the input. TypeScript types are bundled, so `@types/react-calendar-timeline` is no longer needed. And the package is bundled with Vite instead of webpack or rollup, which matters only if you were reaching into the build output.
That is a substantial migration for a component, and it is the reason the project has kept the stable line alive rather than going straight to 1.0. The 0.28.0 tree remains the reference for anyone who cannot make those changes yet.
Every prop must be immutable, and the README says so immediately
The API section opens with a warning that is the single most important thing to know about this component: all props need to be immutable. The README's example is changing an item's title by passing in a whole new items array rather than modifying the title in the old array, and it links to an article on the pros and cons of immutability in React.
This is not a style preference. The timeline holds internal state about what is visible, what is selected and where the viewport sits, and it reconciles that against what you hand it. If you hand it the same array reference with different contents, the reconciliation has nothing to compare and the timeline does not update. Every app using this library has a version of this bug, usually in the form of state that updates in a reducer but never reaches the component because the array identity did not change.
The groups and items shapes are otherwise conventional. A group needs an id and a title, and can carry a rightTitle for the right sidebar, a stackItems flag, and a height in pixels to override the calculated height, which the README suggests is useful for categorized groups. An item needs an id, a group, a title, a start_time and an end_time, plus optional canMove, canResize and canChangeGroup booleans and an itemProps object whose attributes are spread onto the item's root div.
That itemProps detail is what makes the component composable: you can attach data attributes, ARIA values, class names, inline styles and event handlers to individual items without the library needing to know about them. The README's example puts an onDoubleClick handler and a className in there, which is the pattern to copy.
For dates, the README is unambiguous about performance: the preferred and fastest option is Unix timestamps in milliseconds for start_time and end_time. Objects that convert to them, such as a JavaScript Date or a dayjs object, also work but are described as a lot slower.
Viewport control, the buffer prop, and who is responsible for scrolling
There are two ways to say where the timeline looks, and picking the wrong one leaves you with a component that cannot be scrolled.
defaultTimeStart and defaultTimeEnd set where the calendar begins and ends, expecting a Date or dayjs object. visibleTimeStart and visibleTimeEnd set the exact viewport, expecting Unix timestamps in milliseconds, and they carry an obligation: when they are specified, scrolling in the calendar must be orchestrated by the onTimeChange function. The README bolds the requirement that you need to provide either the default pair or the visible pair for the timeline to function at all.
That split is the design. Defaults mean the component owns the viewport until you want it back, at which point you pass visible values and take over the scroll handling. Applications that let the user pan freely across a large time range are the ones that end up on the controlled side, and that is also where onTimeChange needs to write back into your state correctly, which brings the immutability rule back into play one level up.
The buffer prop is the performance lever and it has a surprising side effect. It is a number, defaulting to 3, representing the extra timeline rendered to the left and right of the visible area, which the user scrolls through before the timeline rerenders. Setting it to 1 disables scrolling on the timeline entirely. A buffer prop was also added for calculating when to redraw the canvas, listed among the beta.17 changes with a link to the pull request.
Other viewport-related props mentioned in the API include canvasWidth and canvasHeight, and the timeline also accepts lineHeight, itemHeight, itemMargin, sidebarWidth, stackItems and canMove, canStart, canEnd style booleans. The README documents Timeline Markers and Timeline Headers as their own sections further down, and selection is opt-in: set the selected prop and you take over managing it inside onItemSelect.
How the project is built and tested now
The package.json scripts show a project that has finished its tooling migration. Build runs clean, then tsc, then vite build. Test runs vitest run, with coverage and watch variants alongside. Lint is eslint over .ts and .tsx files in src, and prepare is husky, which is the hook that wires the pre-commit lint-staged formatting the beta.18 notes describe.
The distribution shape is worth a glance because it explains the CSS import path change. The package publishes only dist, and its exports map exposes style.css, a sass condition pointing at Timeline.scss, and a style condition at Timeline.css alongside the ES and CommonJS bundles, with types resolved from dist/index.d.ts. The main field points at a .cjs.js file and the module field at an .es.js file, so bundlers get ESM and Node gets CommonJS. sideEffects is set to false, which lets bundlers drop the CSS import during tree shaking in some configurations and is a plausible reason the stylesheet import is called out so loudly in the migration notes.
The repository layout matches a component library with a real test culture now: src/ for source, __tests__/ and __fixtures__/ for tests, examples/ and demo/ for demos, demo-next/ suggesting a rewrite in progress, doc/ for documentation, and three tsconfig files splitting the main build, the node tooling and the tests. Config files include vite.config.mts, vitest.config.ts, .eslintrc.json, .prettierrc and .husky/.
One leftover is telling: .travis.yml is still in the tree, alongside deploy-gh-pages.sh and deploy-travis-key.enc, even though the CI badge in the README and the GitHub Actions directory in the tree both point at GitHub Actions. Old deployment scripts outliving the CI they served is normal, and it does not mean anything is broken.
GitHub reports the MIT license, which matches the LICENSE.md file at the root, 2,158 stars, 678 forks and just 7 open issues, on the main branch with the last push on 2026-07-24. The repository is not archived.
Where this fits against the alternatives
Search results for React timeline components turn up several neighbours, and the honest comparison is about how much of your application the component owns. This one draws group rows down the left, lays items across a time axis, and lets you drag and resize them, with a canvas rendering layer underneath rather than a plain list of absolutely positioned divs. That canvas is why there is a buffer prop and a redraw calculation at all, and why there is a pinch-zoom crash fix in the most recent release: beta.19 fixes a crash when a second finger lands in the ScrollElement, which is the kind of bug you only find on touch hardware with real fingers.
If you need a scheduler with working-hours calendars and recurring events, this is not that, and nothing in the README claims it is. If you need a read-only visualization of overlapping spans with a sidebar, it is more machinery than you need. What it is genuinely good at is an editable time axis where the user moves things and the layout has to stay fast while they do it.
The beta status is the honest risk. The project has been in the 0.2x series for years with moment, and the 0.30 line is a rewrite of the date layer, the type layer and the bundler at once. Version 0.30.0-beta.17 through beta.19 are small increments on top of a large change, and the one you install will be newer than the one the README describes. Pin it.
Where to start if you are evaluating: install the beta, run the examples directory against your own data shapes, and try the immutability rule early, because it is the failure mode that looks like a bug in the library rather than a bug in your state management.
Editorial conclusion
This is a component that works and a release process that has not quite caught up with it. The canvas timeline, the group sidebar and the drag and resize interactions are the substance of the library, and the beta line makes them usable on React 18 and 19 with bundled types and a much smaller date library. What gives a reader pause is the packaging story: the announcement in the README names one beta number, the package version field names another, and the stable line people are most likely to install still runs on moment and needs its own README. Pin your version explicitly rather than accepting a tag, and read the README from the same tree you installed, because that is the only combination guaranteed to describe your API. The immutability contract is the other thing to internalize before building anything: pass new arrays rather than mutating them, and your timeline updates behave; skip it and you will spend an afternoon on a component that refuses to re-render.
Frequently asked questions
How do I install the beta version of react-calendar-timeline?
Install the beta dist tag with npm install react-calendar-timeline@beta. A plain npm install react-calendar-timeline gives you the latest stable, which the README identifies as 0.28.0 and which still uses moment.js as its date library. The rest of the README documents the 0.30.0 beta API.
Why does my timeline not update when I change an item?
The README states that all props need to be immutable, which means passing a whole new items array rather than mutating the existing one. Change the title by creating a new array containing a new item object, because the timeline reconciles against prop identity and a mutated array gives it nothing to compare.
What changed when migrating from 0.2x to 0.30.0?
Six things: moment replaced by dayjs, the CSS import path moved to react-calendar-timeline/style.css, React 18 or later is required with 16 and 17 dropped, ImmutableJS arrays replaced by plain JavaScript arrays, TypeScript types bundled so no separate types package is needed, and Vite replacing webpack or rollup as the bundler.
Should I pass Unix timestamps or dayjs objects for item start and end times?
Unix timestamps in milliseconds. The README calls that the preferred and fastest option, and notes that objects which convert to them, such as a JavaScript Date or a dayjs object, also work but are considerably slower. defaultTimeStart and defaultTimeEnd are the exception, and there the README expects a Date or dayjs object.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/namespace-ee-react-calendar-timeline)