I needed a timeline for a reporting feature.
Not just something interactive in the browser, but something that could also be rendered server-side and exported (PDFs, images, etc).
It also needed to show dependencies between items, not just a flat set of events.
Seemed straightforward enough.
Started with vis.js
vis.js was a pretty solid choice.
It’s been around forever, lots of features, decent docs. I figured it’d cover everything.
And to be fair it worked fine at first.
Until the requirements stopped being “demo-sized”.
Requirement #1: server-side rendering
This is where things immediately got messy.
The timeline needed to:
- render on the server
- be consistent (same output every time)
- plug into a reporting pipeline
vis.js is very DOM/browser-driven, so SSR was basically:
- not supported unless we also wanted to also have to deal with a headless browser
- possible with hacks I didn’t want to maintain long-term
That alone was a pretty big red flag.
Requirement #2: dependencies between items
This one turned out to be just as awkward.
The timeline wasn’t just visualising events, it needed to show relationships:
- item A blocks item B
- task C depends on task D finishing
- chains of related work across time
In practice, that means links between items, not just items on a timeline.
vis.js doesn’t really handle this cleanly out of the box:
- no first-class concept of dependencies
- you end up hacking it together yourself
- quickly gets messy when the data isn't trivial
Then the data got bigger
Once I started feeding in more realistic data, things slowed down fast.
Nothing crazy either, just enough items to resemble a real product:
- scrolling started to feel heavy
- zooming wasn’t smooth
- lots of DOM nodes getting created
Add dependencies on top of that, and things got even harder to reason about.
It just didn’t feel like something I’d want in production.
React didn’t help much either
Trying to wrap vis.js cleanly in React felt… off.
Lots of imperative code, lifecycle juggling, things getting out of sync.
It worked, but it never felt like it fit.
At that point it was easier to start over
I didn’t originally plan to build a timeline library, but it got to the point where working around everything was more effort than just doing it properly.
So I built Tempis.
The main decision: stop relying on the DOM
Most of the issues came back to one thing:
too much DOM
So I switched to a canvas-based approach instead.
That ended up solving a lot of problems at once:
- far fewer rendering bottlenecks
- smoother interactions
- more predictable output (important for reports)
- a cleaner way to render relationships between items
- it's super easy to render a canvas to an image
What it’s actually aimed at
This isn’t really a “toy timeline” library.
It’s for things like:
- reporting views
- product roadmaps
- activity timelines
- scheduling / Gantt-style interfaces
- anything where you have a lot of time-based data
- and especially where items are connected, not just listed
Top comments (1)
What stood out to me is that the bottleneck wasn’t the timeline component itself.
It was the accumulation of requirements:
SSR,
report generation,
dependency visualization,
large datasets,
React integration,
and predictable rendering.
Individually, each requirement is manageable. Together, they fundamentally change the problem.
That’s often the moment when replacing a library becomes easier than continuing to work around its assumptions.