Guide to fast Next.js



When evaluating the speed of a website, they look at key metrics in a typical user journey:
- Server response time: How long it takes for the user to get any feedback once landed on the page.
- Page render time: How long it takes for a page to become fully visible and interactive.
- User interaction time: How long it takes the user to make key interactions on the page such as navigating between pages or adding an item to the cart.
This article covers tips to measure and make each part of the user journey as fast as it gets.
The basics of site speed: Measuring data correctly
The key to maintaining a fast website for all users, regardless of their device, lies in data. Speed isn't just about user satisfaction; it's crucial for ranking high on search platforms and attracting organic traffic.
To ensure accurate measurement of the right data, tools like Google PageSpeed Insights and Vercel Speed Insights offer objective metrics. These tools diagnose page performance issues, including loading times, interactivity, and visual stability.
Testing the user journey under different network conditions is also essential.
By combining objective tools with hands-on testing, you get a complete view of the website experience, ensuring optimization for all users.
How to speed up server response: Make use of Next.js rendering toolbox
Once key performance metrics are evaluated, the team is able to pinpoint where and how to make improvements in things like server response.
When possible: Pre-render the entire page
Pre-rendering the page at build-time ensures it is served from a CDN instead of your origin server, resulting in the fastest server response possible. This is done automatically by Next.js if you don’t use the edge runtime and the page doesn’t rely on cookies, headers, or search parameters. If you have one server component that fetches a lot of data and makes the entire page slow to load in Next.js then wrap it with Suspense.
Cache fetch requests for fast server responses when using loading spinners
Using loading shells doesn't justify slow server responses. Instead of making users wait for them on every page visit, most server responses can be cached. While this is how fetch requests behave by default in Next.js, you still have control over the freshness of this data:
- By revalidating the server response every x number seconds.
- Or by revalidating the server response when a certain event happens. Here’s an example where a CMS response is revalidated whenever a new CMS page gets published.
The Next.js guide on caching and revalidation and the App Router explainer video are perfect to help understand these concepts.
How to speed up the page render: Minimize client burden
Short answer: Make the browser do the least amount of work to render the page.
After receiving a response from the server, the browser needs to paint the entire page and prepare it for user interactions, like clicking buttons. As it parses HTML and renders, the browser also downloads resources such as CSS, JavaScript, fonts, or images. To speed up page rendering, these tips help reduce the browser's workload.
Reduce the JavaScript bundle size and minimize the impact of hydration
React websites typically include JavaScript comprising React, Next.js, application code containing JSX for every React component, and third-party dependencies.
After the page HTML is rendered and JavaScript is downloaded, React undergoes a process called "hydration," attaching event listeners and state to the page's components.
Just by using React Server Components you already get a speed bump because:
- Their JavaScript (including application code, JSX, and third-party dependencies) is not shipped to the browser.
- React skips their hydration.
When a component needs interactivity, such as state or event listeners, you can use the useClient directive to transform it into a client component. This allows the component to be rendered on the server and also have its JavaScript sent to the browser and hydrated by React.
Reduce the impact of client components on page speed
Only use client components when necessary
You can store component state in URLs without needing to convert it into a client component that relies on React's state.
This approach requires less code to manage the state, turns state buttons into links that function even without JavaScript, and allows the state to persist on page refresh or when sharing the URL.
By shifting from useState to URL state, we lift the state up to the URL for attributes like color, size, and selected image. This enables "deep linking" to specific variations, leveraging the capabilities of the web platform.
Place client components in the leaves of the page tree
To minimize the JavaScript footprint of imported child components, it’s a good practice to place client components the furthest possible at the bottom of the components tree.
Be mindful of third-party dependencies’ bundle sizes
Client components mean more JavaScript for browsers to handle. Tools like pkg-size help compare NPM package sizes, aiding in decision-making.
Lazy-load client components when possible
You can delay downloading heavy client components until they are needed. This can be done through lazy-loading using next/dynamic or React.lazy with Suspense.
Efficiently load third-party scripts
Some third-party dependencies like Google Tag Manager are injected via script tags instead of imports in client components.
@next/third-parties can be used to reduce their impact on page render speed and if dependency is not supported, next/script is also a great option.
How to load fonts more efficiently
Web fonts can be bulky due to unnecessary characters. Using next/font allows for loading local and Google Fonts with optimizations like:
- Only load fonts on pages where they are used.
- Preload fonts to make them available early on when rendering.
- Use display strategies such as swap to avoid blocking text rendering by using a fallback font.
How to load images more efficiently
Short answer: use next/image when you can.
The next/image component provides so many optimizations for local or remote images.
A detailed guide is available on Next.js docs so I’ll only highlight some of them:
- Images are automatically served in modern efficient formats such as AVIF or WebP that preserve quality and dramatically reduce the download size.
- Images are only loaded when visible in the viewport and a
lazyboolean prop is available to do the opposite for critical images. - A
preloadprop is available to make the browser load critical images ASAP. - Images are automatically served in different sizes based on the viewport and props such as
sizesor loaderare available to customise the behaviour. - Local images can automatically show a placeholder while loading and you can provide a
blurDataURLto achieve the same with remote images.
The next/image component is just a very handy utility and is not required to achieve the benefits above:
- Images can still be served in modern formats by using CDNs that can convert them on the fly.
- Lazy-loading images is a native browser attribute that can be used by default.
- Images can be preloaded using a preload link
<link rel="preload" as="image" href="..." />in the document’s heador using ReactDOM.preload. - When loading images from a different domain, it’s a good practice to use preconnect links to inform the browser to establish a connection with the image provider domain early-on.
How to load videos more efficiently
Utilize services like Mux, Cloudinary, or CDNs such as Fastly for optimal video delivery.
Include a poster image for each video, either manually or by extracting the first frame.
Apply image optimization techniques for efficient rendering.
Lazy-load videos to prevent layout shifts and improve user experience.
For videos that don't need to load immediately, they can be lazy-loaded without causing any layout shift.
How to reduce the HTML document size
The HTML document is a critical resource the browser has to download and parse.
Use virtualization
Components such as carousels/sliders, tables, and lists are also usual culprits.
You can use libraries such TanStack Virtual to only render items when they are visible in the viewport while avoiding any layout shifts.
How to speed up user interactions
Short answer: Provide feedback to the user as early as possible.
To prevent slow interactions caused by server responses, consider implementing optimistic UI techniques.
These techniques use JavaScript to display predicted results to users without waiting for the server response.
You can achieve this through standard React state management or by using React’s useOptimistic hook for better performance and immediate feedback.
The importance of a performant website
Speedy websites aren't just more enjoyable and engaging for users. They also directly affect important success metrics like conversion rates and search engine rankings.
While the advice given pertains specifically to Next.js, the fundamental principles can be applied to boost the performance of any website.


