Optimizing Performance in Next.js App Router Applications
The Next.js App Router brings powerful capabilities like React Server Components (RSC), selective hydration, and flexible data caching. However, to achieve a 100/100 Lighthouse score, we need to apply performance optimization patterns correctly.
1. Maximize Server Components usage
Keep interactive parts of your page small and leaves of your component tree. This keeps the Client-side Javascript bundle tiny, since server components are rendered on the server and do not ship runtime dependencies.
// ❌ Bad: Making the whole page a Client Component
"use client";
import React, { useState } from "react";
// ✅ Good: Move interactivity into leaf components
import { Header } from "./Header";
import { Sidebar } from "./Sidebar";
import { InteractiveCounter } from "./InteractiveCounter";
export default function Dashboard() {
return (
<div>
<Header />
<Sidebar />
<InteractiveCounter />
</div>
);
}
2. Optimize Images
Always use Next.js next/image instead of raw <img> tags. The Image component optimizes assets dynamically to modern formats like WebP or AVIF, resizes dynamically, and lazy-loads off-screen elements.
import Image from "next/image";
export function HeroBanner() {
return (
<div className="relative w-full h-96">
<Image
src="/banner.jpg"
alt="Hero Banner"
fill
priority
className="object-cover"
sizes="(max-width: 768px) 100vw, 50vw"
/>
</div>
);
}
3. Dynamic Imports
For heavy third-party components (like map widgets, scheduling calendars, or charts) that are below-the-fold, load them dynamically.
import dynamic from "next/dynamic";
const LazyChart = dynamic(() => import("@/FE/components/HeavyChart"), {
loading: () => <p>Loading Analytics...</p>,
ssr: false,
});
By applying RSC leaf patterns, optimization primitives, and code-splitting, your Next.js application will feel lightning fast and load instantly.
