UISlopKiller Template

Why Your React Animations Feel Slow Even at 60 FPS

An FPS counter can show a steady 60 frames per second while your UI still feels sluggish. Here is how to diagnose real animation bottlenecks.

2 min readUISlopKiller

You open the Performance panel, and an animation runs around 60 FPS. There are no red warnings, yet the interaction still feels slow. According to Jyoti Pathak on DEV.to, an FPS counter only tells you how many frames arrived, not whether they arrived well.

60 FPS Is a Target, Not a Diagnosis

On a 60 Hz display, the browser gets a refresh opportunity roughly every 16.7 milliseconds. But averages hide irregularities. A sequence with occasional slow frames followed by quicker frames can maintain a respectable average while still producing visible hitching. Furthermore, displays running at 120 Hz and 144 Hz call requestAnimationFrame() at faster rates. MDN explicitly recommends using the timestamp provided to the callback when calculating animation progress, as shown below:

let start;

function animate(timestamp) {
  if (start === undefined) {
    start = timestamp;
  }

  const elapsed = timestamp - start;
  const progress = Math.min(elapsed / 600, 1);

  element.style.transform = `translateX(${progress * 200}px)`;

  if (progress < 1) {
    requestAnimationFrame(animate);
  }
}

requestAnimationFrame(animate);

This approach keeps the duration stable when the display refresh rate changes, moving away from hardcoded assumptions about frame counts.

React State vs. Per-Frame Values

React state is great for application logic, but storing every intermediate pixel of an animation there forces component renders to compete with your interaction. Transient per-frame values can instead be handled outside repeated component renders using useRef and requestAnimationFrame().

However, moving code into requestAnimationFrame() does not magically make expensive work cheap. If your callback queries elements, measures layout, and writes styles repeatedly, you are simply asking the browser to execute heavy work during every frame.

Sometimes It Is a Motion Problem

Profiling tools cannot always solve sluggishness. An interaction can maintain a steady frame rate and still feel slow because the motion design itself is too conservative. Changing interpolation values from small increments to faster easing curves can make an interface feel radically more immediate without altering computational costs.

Input-coupled motion exposes latency even faster. If a user's hand provides a reference signal—such as dragging or moving a custom pointer—a visual follower that trails behind will feel like input lag.

Profile Frame Time

Before refactoring your code randomly, record the interaction in your browser's Performance panel. Look for long JavaScript tasks, repeated style recalculations, layout work, or expensive paint operations. Test your animations inside the page where they will actually live, alongside real navigation, typography, images, and third-party scripts.

Remember to check user preferences with prefers-reduced-motion and verify how your effects behave on mid-range mobile devices rather than high-end development machines.

Photo by Christina Morillo, CC0 1.0, via Openverse

Sources