Next.js is a popular and powerful framework built on top of React, designed to help teams build fast, scalable web applications.
Without a performance-first approach, applications can quickly become slow due to excessive client-side Javascript, inefficient data fetching or incorrect rendering strategies. Out of the box, Next.js gives you powerful performance tools.
Web performance in Next.js is commonly the result of multiple choices made throughout the application:
Next.js provides an inbuilt <Image/> component, which extends the native HTML <img> element for optimization.
The next/image component automatically optimizes image size, format, and delivery, helping reduce page weight and improve load times. Offscreen images are deferred automatically (lazily loaded), reducing LCP. The Next.js image component also constrains your image dimensions, preventing layout shifts from occurring when the image loads on the client.
For the same image (1920x1080), PNG file size is 800 KB and WebP is 200 KB!
Out of the box, the <Image/> component is pretty straight forward to use.
<Imagesrc="..."alt="..."width={x-Pixels}height={y-Pixels}//or use fillfill/>
Several optional props can further enhance perceived performance and visual stability:
Next.js allows the user to control the environment where application code can be executed in: the server and the client.
Server-side rendering reduces the amount of JavaScript sent to the browser, improves initial load performance and keeps sensitive logic and credentials out of the client bundle.
Client components ('use client') should only be introduced when the UI requires browser only functionality, such as:
When introducing a Client Component, ensure that you're keeping the client boundary as small as possible and isolate interactivity into focused components.
The client boundary is the point in your React tree where rendering switches from Server → Browser
The fastest JavaScript is the JavaScript you don't need to send. Large client bundles increase download size, parse time and execution cost. All of which delays interactivity and Core Web Vitals
Waterfalls are usually beautiful… but for React developers they can be quite scary. A fetch waterfall is a performance issue that occurs when multiple API calls or fetch requests are chained together, and executed one after another (as opposed to completing them concurrently.
This generally will occur when the page loads first, then data fetching begins, then the UI updates. This effect can be compounded if there are many child components on one page fetching data.
Consequently, this delays page loads 😓
❌ Figure: Bad example - Fetch Waterfall
So how do I avoid recreating Niagara Falls?
Selecting the appropriate rendering strategy is one of the biggest determinants of performance in Next.js.
The easiest way to think about these strategies is on an axis of when the HTML is created
Build -------- Request -------- Browser| | |SSG SSR, ISR CSR
Next.js supports multiple rendering approaches, each suited to a different use case:
Most modern frameworks use Streaming SSR to bridge that gap between pure SSR and CSR