CSS vs. JavaScript • Josh W. Comeau
🌸 I’m running my annual Spring Sale! Ends in 7 hours.I’m running my annual Spring Sale!
CSS vs. JavaScript Exploring the performance implications of different animation strategies<br>Filed underAnimationoninMay 26th, 2026.May 2026.
IntroductionOne of the most common questions around animation performance is whether JS-based animations are slower than CSS-based ones. Should we always strive to use CSS transitions, or is it OK to use JavaScript animation libraries?
There’s a surprising amount of nuance to this question, and I think that the conventional wisdom isn’t quite right. In this post, we’re going to dig into this question and see the differences for ourselves!
Intended audience Some of the code snippets in this tutorial assume that you’re comfortable with typical CSS/JS animation techniques. That said, the main takeaways of this post should be clear to developers of all experience levels.
Link to this headingComparing CSS keyframes to JavaScript loops
Let’s suppose we’re building the following animation:
Play
We can wire this up with a CSS keyframe, like this:
Copy to clipboard@keyframes bounce {<br>to {<br>transform: translateX(calc(var(--bounce-magnitude) * -1));
.ball {<br>--bounce-magnitude: 200px;<br>animation: bounce 1000ms infinite alternate;
(I’m using a CSS transform for this animation because it produces the smoothest motion. In cases where the container size is dynamic, we would need to calculate and apply --bounce-magnitude in JS.)
Alternatively, we could implement this animation using JavaScript! Before we consider JS libraries like GSAP or Motion, let’s start with a plain JS version:
Copy to clipboardconst startTime = performance.now();
const ball = document.querySelector('.ball');
function animate() {<br>const elapsedTime = performance.now() - startTime;
// ✂️ Calculate `x` based on the amount of time that has passed.
ball.style.transform = `translateX(${x}px)`;
window.requestAnimationFrame(animate);
This code uses requestAnimationFrame to run the animate function on every frame (60 times per second on most displays). I’ve cut out the main logic to calculate x since it’s a bit complicated and not relevant for the topic at hand, but you can see the full code(opens in new tab) if you’re curious.
Here’s the question: which approach do you think runs more smoothly?
I think for most of us, our intuitions would tell us that the CSS version is more performant. And our intuition is correct, but maybe not for the reasons we think. 😅
You might think that the JS version is slower because it has to do all that extra work calculating the x value on every frame, or that there’s an extra cost to “crossing the bridge” between JavaScript and the DOM. But modern browser engines can tackle all of that stuff without breaking a sweat; even on low-end devices, that work happens in a tiny fraction of a millisecond, way too quick to affect the framerate of our animation.
But there’s one significant difference: the JavaScript version runs on the main thread, along with everything else happening in our application. CSS transitions and keyframe animations run on a separate thread, so they aren’t disrupted when stuff happens in JavaScript.
I’ve taken the liberty of creating a simulation. Click the “Play” button to run the demo. Every few seconds, the main thread will be blocked. Notice what effect it has on these two animations:
Using CSS Keyframes
Using a JavaScript loop
Play
In modern web applications, the main thread does a lot of work. JavaScript frameworks like React are constantly making updates to the DOM, to keep it in sync with application state. Every time we make a fetch request (eg. to load more data, or to refresh existing data), that response has to be parsed by the main thread.
So, if you’ve ever seen a spinner freeze for a moment before the UI is updated, this is why! JavaScript-based animations have to compete for processing power with the rest of the application.
Why would you do this? For this particular animation, it might seem a bit silly to even try to use JavaScript. Why are we even considering this as an option??<br>In truth, I probably wouldn’t consider using JS to solve this problem; I just needed something simple to use as an example, to explore the trade-offs between CSS and JS.<br>So, please don’t focus too much on the specifics of the bouncing ball. Consider it a stand-in for a more complex animation.
Link to this headingComparing animation libraries
In the example above, I used a requestAnimationFrame loop to update the UI on every frame in JavaScript. This is a pretty low-level technique; in practice, many developers use JavaScript libraries that provide a higher-level abstraction.
Let’s compare two popular animation libraries, Motion(opens in new tab) (formerly Framer Motion) and GSAP(opens in new tab):
Using CSS Keyframes
Using the Motion library
Using GSAP
Play
Huh! Both Motion and GSAP are JavaScript-based, so you might expect...