Engineering

How We Optimized Next.js App Router for 60fps Animations

Kareem El-Malky

Kareem El-Malky

Co-Founder & CTO

June 2, 20265 min read
How We Optimized Next.js App Router for 60fps Animations

1. The Animation Bottleneck

In modern web development, smooth animations are the hallmark of premium digital design. However, combining complex page entry sequences with Next.js App Router transitions often introduces micro-jank.

When a user navigates between routes, Next.js performs client-side routing, mounting and unmounting DOM nodes dynamically. If Framer Motion triggers expensive layouts during these operations, frame rates can plunge from 60fps to below 30fps, leading to stuttering transitions.

Key Culprits: - Layout Shifting (CLS): Triggering animation calculations before dimensions are fully computed by the browser. - Unoptimized Re-renders: Parent containers re-rendering children layout animations repeatedly. - JavaScript Execution Overload: Blocking the main thread with heavy calculation inside animation loops.

---

2. Dynamic Orchestration with Framer Motion

To resolve layout bottlenecks, we implemented a custom orchestration pattern utilizing Framer Motion's AnimatePresence and useReducedMotion to support a wide range of devices.

Here is the code block demonstrating the structure:

tsx
import { motion, AnimatePresence, useReducedMotion } from "framer-motion";
import { useState } from "react";

export function OrchestratedSection({ children }) {
  const prefersReduced = useReducedMotion();
  const [isActive, setIsActive] = useState(true);

  const containerVariants = {
    hidden: { opacity: 0 },
    show: {
      opacity: 1,
      transition: {
        staggerChildren: 0.12,
        delayChildren: 0.2
      }
    }
  };

  const itemVariants = prefersReduced ? {
    hidden: { opacity: 1 },
    show: { opacity: 1 }
  } : {
    hidden: { opacity: 0, y: 20 },
    show: { opacity: 1, y: 0, transition: { duration: 0.6 } }
  };

  return (
    <motion.div
      variants={containerVariants}
      initial="hidden"
      animate="show"
      className="grid grid-cols-1 md:grid-cols-3 gap-6"
    >
      {children}
    </motion.div>
  );
}

---

3. Best Practices for 60fps Performance

  1. 1.Use CSS Transforms over Layout Attributes: Animating properties like width, height, top, or left forces the browser to trigger a full reflow. Instead, animate scale, x, y, and opacity which are handled by the compositor thread.
  2. 2.Prioritize Crucial Assets: Load above-the-fold graphical elements with high loading priority tags to eliminate layout shift during paint events.
  3. 3.Control Stagger Schedules: Limit stagger groups to a maximum of 8 elements. For massive grids, segment elements into viewport slices using dynamic visibility triggers.

The Outcome By switching to compositor-friendly attributes and strict rendering boundaries, our animations achieved a solid 60fps profile across mid-tier Android devices and mobile Safari layouts.

Read Next

Engineering

Scaling PostgreSQL for Real-Time Analytics Dashboard Queries

Practical optimization techniques including partitioning, indexing strategies, and materialized views to handle high write/read loads.

Read Article
Product Design

Designing for the Next Billion Users in the MENA Region

Key product and UX design considerations for startups targeting Arabic-first regions, including typography and localized user flows.

Read Article

Join The Engineering Journal

Get raw insights on shipping high-performance code, twice a month.