Skip to main content

Optimization Tools

Next.js optimizes out of the box: it splits code per route, keeps Server Component code out of the browser, and provides components for the usual causes of slow pages: next/image (resized, lazy-loaded images in modern formats with reserved space, against CLS and for LCP), next/font (self-hosted fonts without layout shift), next/script (third-party scripts with a loading strategy) and next/dynamic (lazy-loaded client components).

Applies to: Next.js 16.4.

Details​

Code Splitting​

  • Each route segment gets its own client bundle; shared code goes into common chunks.
  • Server Components are not sent to the browser at all; only Client Components add JavaScript.
  • next/dynamic (or React.lazy with Suspense) moves a Client Component into a separate chunk that loads when it is rendered.
  • ssr: false skips the server render of a component that needs browser APIs.
  • A library can be loaded on demand with await import('lib') in an event handler.

next/image​

PropWhat it does
src, altRequired. A static import gives width, height and a blur placeholder automatically
width, heightReserve space before loading (no CLS). Required for remote images unless fill is set
fillThe image fills a positioned parent; used with sizes and CSS object-fit
sizesHow wide the image is at each breakpoint; without it the srcset of a fill image assumes 100vw, and a small image downloads a full-width file
loadinglazy by default; eager for images visible on the first screen
preloadAdds a <link rel="preload"> in <head> for the LCP image. Replaces priority, deprecated in 16
placeholderempty (default), blur (with blurDataURL for remote images) or a data:image/... URL
quality75 by default; a value outside images.qualities is rounded to the closest allowed one
  • Images are resized on demand and served as WebP (AVIF if enabled in images.formats) depending on the browser's Accept header.
  • Remote hosts must be allowed in images.remotePatterns.
  • For the LCP image use loading="eager" or fetchPriority="high"; preload only when one known image is the LCP on every viewport. Never leave it lazy.

next/font​

  • Google Fonts are downloaded at build time and, like local fonts, served from your own domain: no request to Google, no extra connection.
  • A fallback font with adjusted metrics is generated, so swapping fonts does not shift the layout.
  • The font is imported by name: Roboto({ weight: ['400', '700'], subsets: ['latin'] }).

next/script​

strategyWhen the script loads
beforeInteractiveBefore hydration; only in the root layout (_document in the Pages Router)
afterInteractive (default)After some hydration on the page
lazyOnloadWhen the browser is idle
workerIn a web worker through Partytown; experimental, Pages Router only

Example​

Examples
app/page.tsx
import Image from 'next/image';
import hero from './hero.jpg';

export default function Page() {
return (
<>
{/* LCP image: preloaded, size and blur placeholder from the import */}
<Image src={hero} alt="Mountains at sunrise" preload placeholder="blur" />

{/* Responsive image in a grid: sizes keeps the download small */}
<div style={{ position: 'relative', aspectRatio: '4 / 3' }}>
<Image
src="https://images.example.com/card.jpg"
alt="Product photo"
fill
sizes="(max-width: 768px) 100vw, 33vw"
style={{ objectFit: 'cover' }}
/>
</div>
</>
);
}
app/layout.tsx
import { Roboto } from 'next/font/google';
import Script from 'next/script';

const roboto = Roboto({ weight: ['400', '700'], subsets: ['latin'] });

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className={roboto.className}>
<body>
{children}
<Script src="https://example.com/analytics.js" strategy="lazyOnload" />
</body>
</html>
);
}
app/dashboard/chart-panel.tsx
'use client';

import dynamic from 'next/dynamic';

// A heavy chart that uses window: separate chunk, no server render
const Chart = dynamic(() => import('./chart'), {
ssr: false,
loading: () => <p>Loading chart...</p>,
});

export function ChartPanel({ show }: { show: boolean }) {
return show ? <Chart /> : null;
}

Follow-up Questions​

  • How does next/image help Core Web Vitals? Reserved width and height prevent CLS; preload or fetchPriority="high" on the hero image improves LCP; lazy loading of the rest saves bandwidth.
  • Why is sizes important with fill? Without it the generated srcset assumes the image is as wide as the viewport, and a small card downloads a full-width file.
  • Why self-host fonts? No extra connection to another origin and no layout shift when the font arrives.
  • next/dynamic or React.lazy? next/dynamic is lazy with Suspense plus the ssr option, and works in both routers. Code is split only when the dynamic import is in a Client Component.

Common Mistakes​

  • preload, loading="eager" or fetchPriority="high" on many images: they all load early and compete with the real LCP image.
  • next/dynamic with ssr: false in a Server Component: a build error; move it into a Client Component.

See Also​