Next.js is a framework built on top of React that provides additional features and capabilities to help you build high-performance web applications. While React is a JavaScript library for building user interfaces, Next.js provides a more complete solution for building server-side rendered (SSR) and statically generated (SSG) web applications.
Overall, Next.js is a powerful and flexible framework that can help you build high-performance web applications with ease. It provides many features and capabilities that are not available in React alone, making it a popular choice for building complex, performance-critical web applications.
Here are some of the best practices you can implement to improve your website’s performance:
- Server-Side Rendering (SSR)
- Code Splitting
- Lazy Loading
- Minification
- Compression
- CDN implementation
- Image Optimization
- Browser Caching
- Remove Unused CSS
Server-Side Rendering (SSR)
Next.js has built-in support for SSR, which allows you to render your website on the server before sending it to the browser. This helps to reduce the time it takes for the website to load and improves its overall performance.
To enable SSR in your Next.js website, you can use the getServerSideProps
function in your pages. For example:
export async function getServerSideProps() {
// fetch data from an API or database
const res = await fetch('https://example.com/data');
const data = await res.json();
// return the data as props
return {
props: {
data,
},
};
}
function Page({ data }) {
// render the page using the data
return (
<div>
<h1>{data.title}</h1>
<p>{data.content}</p>
</div>
);
}
export default Page;
In this example, we’re fetching data from an API or database using the getServerSideProps
function and passing it as props to the Page
component. The Page
component then renders the data on the page.
Code Splitting
Next.js automatically splits your code into smaller chunks and loads only the necessary code for each page, rather than loading the entire code for your entire website. This reduces the load time and makes your website faster.
To optimize code splitting in your Next.js website, you can use dynamic imports. For example:
import dynamic from 'next/dynamic';
const MyComponent = dynamic(() => import('./MyComponent'));
function Page() {
// render the MyComponent dynamically
return <MyComponent />;
}
export default Page;
In this example, we’re using the dynamic
function to import the MyComponent
component dynamically. This means that the component will only be loaded when it’s needed, reducing the load time of the website.
Lazy Loading
Lazy loading is a concept that we can use to reduce the initial load time of a particular page in web applications.
In a normal scenario when the user initially loads the page all the page content will be loaded, however, sometimes users do not care what’s at the bottom of the page and do not bother to scroll. So loading all content will be in vain.
By using lazy loading we render content on demand of the user, so when the user scrolls down we load the content gradually, not during the initial rendering of the page.
There is a detailed explanation of the implementation is Here
Minification
Minification involves removing unnecessary whitespace, comments, and other characters from your code. This reduces the file size of your website and improves its loading speed.
To minify your Next.js website’s code, you can use a minification tool like uglifyjs-webpack-plugin
. For example:
// next.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.optimization.minimizer = [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true,
}),
];
}
return config;
},
};
In this example, we’re using the uglifyjs-webpack-plugin
to minify our JavaScript code. We’re configuring it to use caching, parallel processing, and source maps for easier debugging. We’re also only applying the minification to client-side code by checking the isServer
flag.
You can customize the UglifyJsPlugin
configuration as needed based on your specific use case. Additionally, you may want to consider other minification techniques such as minifying your CSS and HTML code as well.
Compression
Compression involves compressing the files on your website before sending them to the browser. This technique helps to reduce the size of your website and improves its loading speed.
here’s an example of how to compress your Next.js assets using the compression-webpack-plugin
.
// next.config.js
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.plugins.push(
new CompressionPlugin({
test: /\.(js|css|html)$/,
threshold: 10240,
})
);
}
return config;
},
};
In this example, we’re using the compression-webpack-plugin
to compress our JavaScript, CSS, and HTML assets. We’re configuring it to only compress files larger than 10KB (threshold of 10240 bytes) and to only apply compression to client-side code by checking the isServer
flag.
You can customize the CompressionPlugin
configuration as needed based on your specific use case. Additionally, you may want to consider other compression techniques such as brotli compression or server-side compression depending on your hosting setup.
CDN uses
A Content Delivery Network (CDN) stores your website’s static assets on multiple servers around the world. This helps to reduce the load time of your website by serving assets from the server closest to the user.
here’s an example of how to use a CDN to serve your Next.js website’s static assets:
// next.config.js
module.exports = {
// Use a CDN to serve static assets
assetPrefix: 'https://cdn.example.com/',
};
In this example, we’re setting the assetPrefix
configuration option to the URL of our CDN. This tells Next.js to use the CDN URL as the prefix for all static assets (such as images, CSS, and JavaScript files) in our website.
You can customize the CDN URL based on your specific hosting setup. Additionally, you may want to consider using a CDN provider that offers features such as edge caching and global distribution to further optimize the delivery of your website’s static assets.
Image Optimization
Images are often the largest files on a website, and optimizing them can significantly improve your website’s loading speed. You can optimize your images by reducing their file size, compressing them, and using a format that is suitable for the web.
here’s an example that demonstrates the difference between optimizing images using a third-party plugin and using the next/image
component:
// Example using third-party plugin for image optimization
// next.config.js
const withOptimizedImages = require('next-optimized-images');
module.exports = withOptimizedImages({
optimizeImages: true,
optimizeImagesInDev: false,
});
// MyComponent.js
import Image from 'next/image';
function MyComponent() {
return (
<div>
<img src="/my-image.jpg" alt="My Image" />
<Image src="/my-image.jpg" alt="My Image" width={1000} height={500} />
</div>
);
}
In this example, we’re using the next-optimized-images
plugin to optimize images in our Next.js application. We’re also using the next/image
component to display an image.
The first image is displayed using a regular img
element with the src
attribute set to the path of the image file. This image will not be optimized by the next-optimized-images
plugin.
The second image is displayed using the next/image
component with the src
attribute set to the path of the image file and the width
and height
attributes set to the desired dimensions of the image. This image will be automatically optimized by the sharp
library and the next/image
component.
By using the next/image
component, you can take advantage of built-in image optimization features such as automatic lazy loading, responsive image sizes, and optimized image formats like WebP. Additionally, the next/image
component will automatically handle image optimization for you, so you don’t need to use a separate plugin or tool.
However, if you have specific image optimization requirements that are not met by the next/image
component, you can use a third-party plugin like next-optimized-images
to optimize your images. This gives you more control over how your images are optimized and how they’re delivered to the browser.
Browser Caching
Browser caching involves storing static assets like images, CSS, and JavaScript files in the browser’s cache. This allows the browser to load your website faster when the user revisits it.
here’s an example of how to set up browser caching in your Next.js application using the cache-control
HTTP header:
// pages/_app.js
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { ServerResponse } from 'http';
function MyApp({ Component, pageProps }) {
const router = useRouter();
useEffect(() => {
// Set up browser caching for all pages
router.beforePopState(({ as }) => {
if (typeof window !== 'undefined') {
window.history.replaceState({ as }, '', as);
}
return true;
});
// Set up browser caching for static assets
if (typeof window !== 'undefined') {
const isProd = process.env.NODE_ENV === 'production';
if (isProd) {
const addHeaders = (res: ServerResponse) => {
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
};
addHeaders(window.document);
const origWrite = document.write;
document.write = (...args) => {
addHeaders(document);
origWrite.apply(null, args);
};
}
}
}, []);
return <Component {...pageProps} />;
}
export default MyApp;
In this example, we’re using the cache-control
HTTP header to set up browser caching for all pages and static assets in our Next.js application. We’re setting the max-age
directive to 31536000 seconds (1 year) to ensure that the browser caches the assets for a long time.
To set up browser caching for all pages, we’re using the router.beforePopState
method to intercept the back button and reload the page from the browser cache. This ensures that the page is loaded quickly and efficiently from the cache instead of being reloaded from the server.
To set up browser caching for static assets, we’re using the ServerResponse
object to set the Cache-Control
header on the response. We’re also intercepting the document.write
method to add the Cache-Control
header to the HTML document.
By setting up browser caching in this way, you can ensure that your Next.js application loads quickly and efficiently for your users, especially on subsequent visits or when they navigate back to a previous page.
Remove Unused CSS
Removing unused CSS helps to reduce the file size of your website and improve its loading speed. You can use tools like PurgeCSS to remove unused CSS from your website.
here’s an example of how to remove unused CSS in your Next.js application using the purgeCSS
library:
// next.config.js
const withPurgeCss = require('next-purgecss');
module.exports = withPurgeCss({
purgeCssEnabled: ({ dev, isServer }) => !dev && !isServer,
purgeCssPaths: [
'pages/**/*.{js,jsx,ts,tsx}',
'components/**/*.{js,jsx,ts,tsx}',
],
});
In this example, we’re using the next-purgecss
plugin to remove unused CSS from our Next.js application. We’re configuring the plugin to only run in production mode (not in development mode or on the server) using the purgeCssEnabled
option. We’re also specifying the paths to the files that should be analyzed for unused CSS using the purgeCssPaths
option.
Once you’ve set up the next-purgecss
plugin, you can run a production build of your application using the next build
command to generate optimized CSS files.
By removing unused CSS, you can reduce the size of your CSS files and improve the performance of your Next.js application. This can lead to faster page load times, better user experience, and improved SEO.