Knowledge Hub
For manager
Headless CMSFor developer
Web FrameworksStatic Site GeneratorsServerless DatabasesMonoreposHostingPIMLearn
GuidesTutorialsAdopt
Case studiesNext.js
Written by Thom Krupa, Melvin Kosisochukwu
Last update: 6/11/2024
Feature | Next.js | |
---|---|---|
Written in | JavaScript | |
Template Language The syntax or language used to define the layout and structure of web pages. | JSX | |
Based on JS framework | React | |
Built-in module bundler | Turbopack | |
Static pages (SSG) | ||
Dynamic pages (SSR) | ||
Developer Experience | ||
TypeScript support | ||
Serverless Functions (API) Small pieces of code that run on-demand without managing servers, typically used for API endpoints. | ||
Focus on plugin system | ||
Themes ecosystem | ||
Hot reloading The ability to instantly see changes in the browser without refreshing the page when developing. | ||
Code Splitting The ability of splitting code into smaller chunks to load them only when needed, improving performance. | ||
Content Preview Allows content editors to see live changes to draft content in a staging environment before it goes live. | ||
Builit-in Optimizations | ||
Third-party Script Optimization Optimizing external scripts (like analytics or ads) to improve the performance and loading speed of your website. | ||
Image Optimization | ||
An option to disable runtime JS For certain use cases, like static HTML + CSS websites where interactivity isn't needed, shipping JavaScript is unnecessary. | experimental feature | |
Critical CSS Extraction Extracting only the CSS needed for the initial page load to improve performance. | experimental feature | |
Starters or examples with common use cases | ||
Data fetching Methods to fetch data from APIs or other sources, either at build time or runtime. | ||
10+ Headless CMS examples | ||
Authentication | ||
Adding search | ||
Ecommerce | ||
Security | ||
Regular security audits by external researchers | ||
Environment Variables Variables used to configure applications based on different environments (development, production). | ||
Content Security Policy (CSP) | Community example |
It was fall of 2016 when I discovered the first version of Next.js. I created a simple file that looked like this:
// pages/index.js
export default () => <div>Hello world!</div>
I executed in the terminal and the magic happened. Two lines of code, one command later, and my new hello world website went live. It was hard to compare that experience to developing custom WordPress themes. Next.js and Now (currently Vercel) felt incredibly easy. Almost like cheating.
A few months later, I shipped my first project using Next.js and the WordPress API. The project had some serious performance issues. Fetching data on every request from many API endpoints wasn't the best idea. I probably should export those pages to static ones, right? But it wasn't trivial in the first versions of Next.js.
Next.js evolved to support static site generation. Vercel evolved to support serverless.
Version 9.3 introduced three new data fetching methods:
Below is a simple example of static generation:
// pages/posts/[slug].js
// [slug] filename means it's a dynamic parameter
// it will be passed to getStaticProps `params` object
const BlogPost = ({ data }) => <Post content={data} />
// executed only at build time
export async function getStaticPaths() {
const response = await fetch('https://api.domain/posts')
const data = await response.json()
// all paths we want to pre-render
const paths = posts.map((post) => ({
params: { slug: post.slug }
}))
return { paths }
}
// executed only at build time
export async function getStaticProps({ params }) {
const response = await fetch(`https://api.domain/posts/${params.slug}`)
const data = await response.json()
return { props: { data, fallback: false } }
}
export default BlogPost
The code above fetches from the API all blog posts at build time and creates array with for each of them. Data for each single blog post is fetched in based on the param.
If you are familiar with Gatsby, you can notice that is a bit like in . But I think Next approach is easier to understand. The difference is that you don't need to specify a path to a template and passing in . In Next, everything is in the same file. In this case, value is accessible through a query parameter. To learn more check the Migrating from Gatsby article.
If you add a new post you need to re-build the project unless you change to . If there is no existing HTML file on the CDN, Next.js will try to fetch content on the client and cache it. Fallback is very useful if you have a large collection of posts that updates frequently.
Next.js 10, among other things, has introduced a new, built-in image component and optimization. From now on you don't have to worry about shipping large images for mobile devices. Next handles resizing and generates a modern WebP image format that is approximately 30% smaller than JPG. If your website has a lot of images this can greatly reduce bandwidth and client-side performance.
To use component import it from :
const Hero = () => (
<section>
<Image src="/assets/cute-hero.png" width={500} height={500} />
<h1>Hello world!</h1>
</section>
)
export default Hero
Next.js' approach doesn't affect build time at all. All optimizations are done at request time. Currently, Next supports 4 cloud providers: Vercel, Imgix, Cloudinary, and Akamai.
The following are some of the new features that were introduced in Next.js 13:
The app directory in Next.js is a new feature introduced in version 13. It allows you to create a separate directory for your application code, which can help improve the performance and scalability of your application.
The app directory is located at pages/app. This directory contains all of the files that make up your application, such as components, pages, and layouts.
When you use the app directory, Next.js will automatically pre-render all pages at build time. It can improve the performance of your application for users who visit your site for the first time.
The app directory also helps to improve the scalability of your application. Separating your application code from pages can make it easier to scale your application horizontally.
What do you stand to gain by using the app directory in Next.js?
React Server Components (RSCs) are a new feature that allows you to render React components on the server. This can be used to improve the performance of your application, as the server can render the components before they are sent to the client.
RSCs are similar to regular React components but there are a few differences. First, RSCs must be exported as a function. Second, RSCs cannot access any state or props that are not defined in the function's parameters.
Here is an example of a React Server Component:
export default function MyServerComponent(title) {
return (
<div>
<h1>{title}</h1>
</div>
);
}
This component takes a title prop as input and renders a heading with the title.
To use an RSC, you need to wrap it in a StaticRoute
component. The StaticRoute
component takes a path and an RSC as input.
Here is an example of how to use an RSC in Next.js:
import MyServerComponent from './MyServerComponent';
export default function Home() {
return (
<StaticRoute path="/" component={MyServerComponent} />
);
}
This code will render the MyServerComponent
component when the user visits the /
path.
In Next.js, a layout is a React component shared across multiple pages. Layouts can be used to share common UI elements, such as a header, footer, or sidebar. They can also be used to apply global styles to your application.
There are two ways to define a layout in Next.js:
You can define a global layout by exporting a React component from a layout.js
file. This layout will be applied to all pages in your application.
Here is an example of a global layout in Next.js:
export default function Layout() {
return (
<div>
<header>
<h1>My Site</h1>
</header>
<main>{children}</main>
<footer>
<p>Copyright (c) 2023</p>
</footer>
</div>
);
}
This layout defines a header, main, and footer section. The children
prop is a placeholder for the content of the page.
You can also define a per-page layout by adding a getLayout
prop to your page component. This prop should return a React component that will be used as the layout for that page.
Here is an example of a per-page layout in Next.js:
export default function Page() {
const layout = getLayout();
return (
<layout>
<h1>This is my page</h1>
</layout>
);
}
This component defines a heading with "This is my page" text. The getLayout prop returns the global layout which is then used to wrap the content of the page.
Streaming is a feature in Next.js that allows you to send data to the client as it becomes available. It improves your application's performance by reducing the time the client needs to wait for the entire page to load.
Streaming is supported in the latest version of Next.js, which is 13.4. To use streaming, you need thegetInitialProps prop on your page component. The getInitialProps prop is a function called when the page is first requested. In this function, you can use the fetch API to fetch data from an API server.
Once you have fetched the data, you can use the stream method to send it to the client. The stream method takes a function as an argument. This function will be called repeatedly with chunks of data as they become available.
Here is an example of how to use streaming in Next.js:
import { Poppins } from "next/font/google";
import { Suspense } from "react";
interface ITodo {
userId: number;
id: number;
title: string;
completed: boolean;
}
const poppins = Poppins({ weight: "600", subsets: ["devanagari"] });
const TodoItem = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const data = await res.json() as ITodo;
return <div>{data?.title}</div>;
};
export default function Posts() {
return (
<main className="flex min-h-screen flex-col gap-4 items-center p-24">
<h1 className={poppins.className}>Hello World</h1>
<section>
<Suspense fallback={<p>Loading todo...</p>}>
<TodoItem />
</Suspense>
</section>
</main>
);
}
This code will fetch data from the API server and stream it to the client. The client will then render the data as it becomes available. The Suspense component is used to wrap an asynchronous component and display a fallback spinner or skeleton until the asynchronous process is completed. Then it will swap the fallback component for the component wrapped in Suspense.
Streaming can be a great way to improve the performance of your Next.js application. It can also be used to create more interactive and engaging user experiences.
Here is what you stand to gain by using streaming in Next.js:
The @next/font package is a Next.js module that allows you to optimize your fonts, including custom fonts. It also removes external network requests for improved privacy and performance.
The @next/font
package is built into Next.js 13.2, so you don't need to install it separately. It allows you to use Google Fonts pre-built or custom local fonts in your file directory.
import { Poppins } from "next/font/google";
const poppins = Poppins({ weight: "600", subsets: ["devanagari"] });
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<h1 className={poppins.className}>Hello World</h1>
</main>
);
}
The code block above enables using the Poppins font from Google Fonts. We can also use local fonts:
import localFont from "next/font/local";
const font = localFont({
src: "/fonts/poppins-v15-latin-regular.woff2",
});
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<h1 className={font.className}>Hello World</h1>
</main>
);
}
The local fonts requires an option object for the font properties, as shown in the code block above. The src
key should be assigned the file path of your font located in your project directory.
The @next/font
package also includes other features, such as:
@next/font
package will automatically fallback to a default font.@next/font
package can self-host your fonts, which means that you don't need to upload them to a CDN. This can improve performance and privacy.@next/font
package can optimize your fonts for performance. It includes minifying the font files and removing unused glyphs.The next/link and next/image components in Next.js have been improved in a number of ways in recent versions. These improvements include:
next/link
and next/image
components now use native browser features for lazy loading and image optimization, which can improve the performance of your application;next/link
and next/image
components now support a wider range of props, which gives you more flexibility in how you use them;next/link
and next/image
components now have better accessibility support, which makes your Next.js application more accessible to users with disabilities;next/link
does not require you to nest an anchor (<a>
) tag within the Link component, creating a better and improved development process;There are several differences in the handling of certain elements in Next.js 13 compared to its previous version, 12. Apart from the recent enhancement and addition to the file structure with the app directory, changes have also been made to Layouts and the handling of routing in Next.js13. Moreover, version 13 provides an improved method for managing fonts, a topic we've covered in previous sections.
Here is a table that summarizes the key differences between Next.js 13 and the previous version:
Feature | vs Next.js 13 | vs Previous version |
---|---|---|
Image component | new Image component with improved performance and optimization | next/image component with less client-side JavaScript |
Font system | new Font system with automatic optimization and removal of external network requests | next/fonts package |
App Router | new App Router with improved flexibility and power | pages/index.js file |
TypeScript support | improved TypeScript support with better type checking and more efficient compilation | basic TypeScript support |
Other improvements | faster builds, faster refresh, faster startup, improved accessibility, and more flexible redirects and rewrites | no other major improvements |
The latest Next.js Version 13 offers an enhanced upgrade from its predecessor, boasting exciting new features and substantial performance improvements. However, with these developments come certain trade-offs compared to the previous version. In the following section, we will delve into a detailed review of these advantages, improvements, and the associated trade-offs.
Here are some of the trade-offs and advantages of using Next.js 13:
Next.js is zero-config from the very beginning.
nextjs13
├── app
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── public
│ ├── next.svg
│ └── vercel.svg
├── .eslintrc.json
├── .gitignore
├── README.md
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── tailwind.config.js
└── tsconfig.json
Every file in directory is a lambda function.
Next.js has a great, growing community. You can read some interesting conversations and discuss RFC on Github. Vercel's team is very clear about the framework direction and open to community suggestions.
There is a lot of examples showing integration with different tools like Headless CMS, CSS-in-JS, or auth.
The easiest and recommended way to start a new Next.js project is to use:
npx create-next-app
# or
yarn create next-app
If you want to learn more, I highly recommend the official Learn Next tutorial.
It helps to understand how to navigate between pages, add static assets, and fetch data. Once completed you will be ready to build your first Next.js application!
The recommended platform is, of course, Vercel. Its CDN is designed at the edge to support features like incremental static generation, where pages first are populated into the durable store (S3), then the pathname is purged to ensure users are able to see the most recent content.
Preview mode works seamlessly on the Vercel platform, as well as the fallback feature. You can connect your repository and everything works out of the box with no additional config needed.
If for some reason you don't want to use Vercel, it is possible to deploy Next.js on every modern hosting platform like Netlify, Render, AWS, DigitalOcean, and so on. Netlify maintains a special next-on-netlify package that enables the server-side rendering of pages. Incremental static regeneration, fallback, and preview don't work exactly like on Vercel, so it's not a 1-to-1 replacement.
With Next.js you can build a full spectrum of websites and apps. From simple marketing pages and blogs to eCommerce and PWA with cookie-based authentication.
It's great for projects that require flexibility in how you build specific parts of the website. First-world static site generator with capabilities of dynamic enhancement. Hybrid mode is what really shines. Does it mean it's perfect for everything? Not at all. If you don't need React and don't plan to update the project frequently it might be overkill.