Next.js

Next.js started out as a small, zero-config framework for server-rendered universal JavaScript web apps, built on top of React, Webpack, and Babel. Initially, it was focused on enabling a smooth universal JavaScript experience. Over the years, it evolved to support static site generation, TypeScript, dynamic pages, serverless functions, and more.

Written by:
  • Thom Krupa

    Thom Krupa

Last update: October 29, 2020
  • Next.js logo
  • JS framework: React
  • Core Maintainer: Vercel, Inc.
  • Website: nextjs.org
  • Founded: 2015
  • Github Stars:
  • npm downloads:

Last update: October 29, 2020

Next.js 1.0 - the beginning

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
import React from 'react'
export default () => <div>Hello world!</div>

I executed now 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.

Fast-forward to Next.js 9.3

Next.js evolved to support static site generation. Vercel evolved to support serverless.

Version 9.3 introduced three new data fetching methods:

  • getStaticProps to fetch data at build time, this can be content for a single post
  • getStaticPaths to specify a collection of dynamic routes, for example, a list of blog posts
  • getServerSideProps to fetch data on every request.

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 paths array with slug for each of them. Data for each single blog post is fetched in getStaticProps based on the slug param.

If you are familiar with Gatsby, you can notice that getStaticProps is a bit like createPages in gatsby-node.js. 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 slug in context. In Next, everything is in the same file. In this case, slug 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 fallback to true. 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.

Image component

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 <Image /> component import it from next/image:

import Image from 'next/image'

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.

File Structure

Next.js is zero-config from the very beginning.

├── package-lock.json
├── package.json
├── pages
│   ├── about.js
│   ├── blog
│   │   ├── [slug].js
│   │   └── index.js
│   └── api
│       ├── posts.js
│       ├── about.js
│       ├── blog.js
│       ├── contact.js
│       └── index.js
└── public
    └── logo.png

Every file in src/api directory is a lambda function.

Ecosystem

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.

Showcase

  • TikTok

    Visit
  • Hulu

    Visit
  • Auth0

    Visit
  • AT&T

    Visit
  • Backlinko

    Visit
  • Elastic

    Visit

How to get started?

The easiest and recommended way to start a new Next.js project is to use create-next-app:

npx create-next-app
# or
yarn create next-app

That one command sets up the project for you. Your development environment will be available at http://localhost:3000.

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!

Deploying Next.js

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.

Conclusion

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.

Features
  • Zero-config
  • Great perceived performance thanks to instant route changes and prefetching.
  • TypeScript support.
  • Automatic code splitting.
  • Dynamic API routes.
  • Vercel team constantly works on reducing JS bundle size and optimizes performance.
  • Built-in integration with the new Web Vitals metrics. Next.js analytics.
  • Hybrid mode: both static generation and SSR.
  • Built-in image component and image optimization on demand.
  • First-class support for internationalization.
Use Cases
  • Hybrid apps. If you need both static and server-side rendered pages on each request.
  • eCommerce. Especially, with big traffic and frequent content updates.
  • Landing pages and marketing websites. Especially, if you already use React and have a design system.
  • Websites with a lot of interactive React components.
  • Websites with a lot of advanced route transitions.