Next.js is now considered the framework for building the modern Internet. Its built-in features, server-side rendering capabilities, and seamless integration with React have made it a top 3 solution among developers. At the same time, tuning a Next.js production application requires some work to achieve high-level performance, short loading times, and an excellent user experience.
In this guide, you will dig into the essential strategies and best practices to deploy Next.js to production!
7 Best Practices to Follow Before Deploying Next.js to Production
Let’s explore the most important best practice to optimize Next.js for production.
1. Take Advantage of Next.js’ Built-In Optimization Features
Next.js comes with built-in optimization features to take the performance of your production application to the next level. All you have to do to leverage them is to integrate them into your project, as explained in the documentation. These include:
- Image Optimization:
next/image
exposes an<Image>
component that extends the traditional HTML<img>
element with automatic image optimization capabilities. In detail, it allows Next.js to:- Serve images in the best size depending on the user’s device.
- Automatically convert them to modern image formats like WebP and AVIF.
- Prevent layout shift errors when loading images in the frontend.
- Load them only when they enter the viewport, using native browser lazy loading.
- Resize images on-demand, even for remote files.
- Font Optimization:
next/font
optimizes local fonts and removes external network requests for improved privacy and performance. As of Next.js 13, it supports self-hosting on any Google Font to avoid making requests to retrieve font files from Google. - Script Optimization:
next/script
offers a<Script>
component that extends the traditional HTML<script>
tag by allowing JavaScript scripts to be loaded only once across multiple pages.
2. Configure Caching and Use Incremental Static Regeneration Whenever Possible
Next.js treats file inside the /public
folder as static assets. For example, if you store a logo.png
image file in /public
, you can then reference it in your code as /logo.png
:
<Image src="/logo.png" alt="Company's Logo" width="200" height="200" />
Code language: HTML, XML (xml)
Usually, /public
contains static images, JavaScript, CSS, and other media files. Caching these resources is a good way to improve response times and reduce the number of requests required to render a page.
Here is why, by default, Next.js add the following header in production for those assets:
Cache-Control: public, max-age=31536000, immutable
Code language: PHP (php)
This instructs the browser to cache those files for one year. So, if your Next.js site relies on some static resources, you should download and place them inside /public
.
Next.js also provides the minimumCacheTTL
configuration to specify the TTL (Time To Live) in seconds for caching images optimized through <Image>
. Set it in next.config.js
as follows:
// next.config.js
module.exports = {
images: {
// cache optimized images for 60 seconds
minimumCacheTTL: 60,
},
}
Code language: JavaScript (javascript)
Similarly, Next.js can cache statically generated pages through Incremental Static Regeneration (ISR). Thanks to this feature, new static pages can be created or updated after the application has been built.
To enable ISR, set the revalidate
option in getStaticProps()
:
export async function getStaticProps() {
// ...
return {
props: {
// ...
},
// Next.js will re-generate this page
// when a request comes in, at most once
// every 10 seconds
revalidate: 10,
}
}
Code language: JavaScript (javascript)
This is how ISR works:
- The site initially shows the pre-rendered page generated at build time.
- Within 10 seconds, it will continue to display the initial page.
- When a request arrives after 10 seconds from the last regeneration, the framework triggers a background regeneration of the page.
Note that a Next.js production app only regenerates static pages that are requested by users to save resources.
3. Integrate an Analytics or APM Tool
Once a Next.js site is in production, you need a way to track its performance. In particular, you should have a system in place to monitor page views and get information about site traffic.
When deploying to Vercel, you can achieve that with Next.js Speed Insights. This tool allows you to analyze and measure the performance of your application’s pages using various metrics. To enable it:
- Turn on the Web Analytics option in the Vercel Dashboard.
- Add the
@vercel/analytics
package to your project’s dependencies withnpm i @vercel/analytics
- Use the
<Analytics />
component to inject the analytics script into your app. - Deploy your app to Vercel, and data should start flowing to the Analytics view.
Similarly, you need a service that tracks site performance, alerts you when something goes wrong or the site goes offline, and collects information about bugs and runtime errors. This is what Application Monitoring (APM) is all about.
Some of the most popular APM libraries for Next.js are Sentry, New Relic, and AppSignal.
4. Set Up a Logging System
To keep track of what is going on in a Next.js production app, you must add some logs to your code. The easiest way to log messages on both the client and server is using the methods exposed by the JavaScript console
object. The most popular ones are:
console.clear()
: Clears the browser console.console.log()
: Logs general information.console.debug()
: Logs a debug message in the browser console and a regular message on the server.console.error()
: Logs an error message.console.warn()
: Logs a warning message in the browser console or an error message on the serverconsole.time()
: Starts a timer that can be used to compute the duration of an operation.console.timeEnd()
: Stops the timer and prints the duration of the operation.
If you instead prefer a more structured solution, Next.js recommends pino
. This is a fast and lightweight JavaScript logging library designed for high-performance applications.
5. Enable Error Handling With Custom 500 and 400 Pages
Like any other application, Next.js sites are subject to errors. One of the most important aspects of error handling is presenting meaningful error messages to users to inform them of what happened. When an error occurs in the frontend or backend, Next.js displays this static 500 page:
As you can see, this page is not informative at all and may result in a bad user experience for visitors. This is why Next.js supports custom 500 pages.
If you are a Pages Router user, create a 500.js
page under /pages
:
// pages/500.js
export default function Custom500Page() {
// your custom 500 page component....
}
Code language: JavaScript (javascript)
This represents the frontend page components that will be shown to users in case of errors.
If you are an App Router user, create an error.js
file under /app
:
// app/error.js
'use client' // error components must be client components
export default function CustomErrorPage({
error,
reset,
}) {
// your custom error page component....
}
Code language: JavaScript (javascript)
Note that this must be a client component.
Next.js also supports a static 404 page, which is rendered:
- When the object returned by
getStaticProps()
orgetServerSideProps()
containsnotFound: true
in a Pages Router app. - When the
notFound()
function is called in an App Router app.
Again, the default 404 page is not the best:
To customize it, if you are a Pages Router user, create a 404.js
page under /pages
:
// pages/404.js
export default function Custom404Page() {
// your custom 404 error page component....
}
Code language: JavaScript (javascript)
If you are an App Router user, define a not-found.js
file under /app
:
// app/not-found.js
export default function NotFoundPage() {
// your custom 404 error page component....
}
Code language: JavaScript (javascript)
6. Reduce the Size of the Build Bundle
Producing a minimized bundle is great, as clients will take less time and network bandwidth to download and render the Next.js production app.
During the next build
task, Next.js generates an optimized JavaScript bundle for each page.
The goal is to try to reduce those bundles to the bare while preserving functionality. For this purpose, you can use dynamic imports via next/dynamic
to lazy-load JavaScript resources. This mechanism allows you to defer loading specific components or libraries until the user performs a particular operation on the page.
To analyze the bundle produced by Next.js and get guidance on how to reduce its size, you can use the following tools:
- Webpack Bundle Analyzer: To visually explore the size of webpack output files in an interactive treemap.
- Package Phobia: To analyze the cost of adding a new dependency to your project.
- Bundle Phobia: To analyze how much a dependency will increase the bundle size.
- bundlejs: To quickly bundle and minify your project in your browser.
- Import Cost: To display the size of any imported package inside Visual Studio Code.
7. Optimize Page SEO Performance With Lighthouse
The ultimate goal of most Next.js sites is to produce excellent SEO results. Google has changed its approach to SEO performance evaluation a great deal over time, and it now focuses primarily on Core Web Vitals:
- Largest Contentful Paint (LCP): Measures the time it takes for the main content of a page to become visible to users.
- First Input Delay (FID): Evaluates the time delay between a user’s first interaction and the browser’s response.
- Cumulative Layout Shift (CLS): Gauges the visual stability of a page by measuring the number of unexpected layout shifts that may annoy users.
Recommended article: React Libraries for Boosting UI Design
These represent user experience metrics that Google uses to assess and quantify the overall performance of web pages and define their ranking in search results.
The best tool for optimizing these indicators is Google Lighthouse, an open-source tool built into Chrome that can be run on any web page to check its SEO performance.
To optimize Next.js for production, you should build the project, start it, open it in Incognito mode in the browser, and launch Lighthouse on each page. This will provide guidance and best practices for improving site performance, accessibility, and SEO.
Conclusion
In this article, you looked at what to do to make a Next.js application production-ready. As you learned here, this involves enabling caching, using the advanced built-in optimization features, and setting up a performance logging and monitoring system.
Through several examples and insights, you had the opportunity to see how to build a reliable Next.js production site. Deploying a top-notch site has never been easier!
Thanks for reading! We hope you found this article helpful!