Performance
Why Your Animations Stutter - Batch Your DOM Style Changes
February 18, 2026
Your dropdown animates smoothly on your machine. On your teammate's laptop, it stutters. On mobile, it feels sluggish.
Often the culprit isn't the animation itself - it's how you're applying styles. Every individual style change can force the browser to recalculate layout (reflow) and repaint. Do it four times, and you get four reflows. Do it once, and you get one.
The problem: one property, one reflow
function updateElementStyles(element: HTMLElement) {
element.style.width = "100px"; // reflow
element.style.height = "200px"; // reflow
element.style.backgroundColor = "blue"; // reflow
element.style.border = "1px solid black"; // reflow
}Each assignment can trigger a layout pass. The browser doesn't know you're about to change more - it recalculates after every change. That adds up fast.
The fix: batch your changes
Option 1: Use a class (best)
// CSS
.highlighted-box {
width: 100px;
height: 200px;
background-color: blue;
border: 1px solid black;
}
// JavaScript
function updateElementStyles(element: HTMLElement) {
element.classList.add('highlighted-box')
}One class toggle. One reflow. Classes are cached by the browser and keep your logic separate from your styles.
Option 2: Use cssText
function updateElementStyles(element: HTMLElement) {
element.style.cssText = `
width: 100px;
height: 200px;
background-color: blue;
border: 1px solid black;
`;
}Same idea - apply all changes in one shot instead of four.
In React: avoid the ref + useEffect pattern
// Incorrect: mutating styles one by one in useEffect
function Box({ isHighlighted }: { isHighlighted: boolean }) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (ref.current && isHighlighted) {
ref.current.style.width = "100px";
ref.current.style.height = "200px";
ref.current.style.backgroundColor = "blue";
}
}, [isHighlighted]);
return <div ref={ref}>Content</div>;
}This pattern is easy to reach for when you "need" imperative DOM access. You usually don't.
// Correct: let React handle it with a class
function Box({ isHighlighted }: { isHighlighted: boolean }) {
return <div className={isHighlighted ? "highlighted-box" : ""}>Content</div>;
}React batches updates. CSS classes batch styles. Together they keep reflows to a minimum.
Rule of thumb: Prefer classes over inline styles. Batch your DOM changes. Your users' devices will thank you.
Related content
Lazy-Load vs Conditional Mount/Unmount - What Actually Changes
Lazy-loading defers code/data download; conditional rendering decides whether a component instance exists. The difference impacts network, effects, and state.
Read moreStop Computing the Same Thing Twice - Cache Your Function Results
Your notification feed formats "2 hours ago" fifty times for fifty items. Most share the same timestamp. A simple cache cuts the work to a handful. Here's when to use a Map vs React's cache().
Read moreUse Set for Membership Checks - Not .includes()
You're filtering items by allowed IDs. Each check does array.includes() - O(n) per item. A Set gives you O(1). Same result, much faster.
Read more