Skip to content

Getting started with Next.js, a tutorial

New Course Coming Soon:

Get Really Good at Git

Next.js is a very popular Node.js framework which enables an easy server-side React rendering, and provides many other amazing features

Introduction

Working on a modern JavaScript application powered by React is awesome until you realize that there are a couple problems related to rendering all the content on the client-side.

First, the page takes longer to the become visible to the user, because before the content loads, all the JavaScript must load, and your application needs to run to determine what to show on the page.

Second, if you are building a publicly available website, you have a content SEO issue. Search engines are getting better at running and indexing JavaScript apps, but it’s much better if we can send them content instead of letting them figure it out.

The solution to both of those problems is server rendering, also called static pre-rendering.

Next.js is one React framework to do all of this in a very simple way, but it’s not limited to this. It’s advertised by its creators as a zero-configuration, single-command toolchain for React apps.

It provides a common structure that allows you to easily build a frontend React application, and transparently handle server-side rendering for you.

Main features

Here is a non-exhaustive list of the main Next.js features:

Installation

Next.js supports all the major platforms: Linux, macOS, Windows.

A Next.js project is started easily with npm:

npm install next react react-dom

Getting started

Create a package.json file with this content:

{
  "scripts": {
    "dev": "next"
  }
}

If you run this command now:

npm run dev

the script will raise an error complaining about not finding the pages folder. This is the only thing that Next.js requires to run.

Create an empty pages folder, and run the command again, and Next.js will start up a server on localhost:3000.

If you go to that URL now, you’ll be greeted by a friendly 404 page, with a nice clean design.

A 404 page returned by Next.js

Next.js handles other error types as well, like 500 errors for example.

Create a page

In the pages folder create an index.js file with a simple React function component:

export default () => (
  <div>
    <p>Hello World!</p>
  </div>
)

If you visit localhost:3000, this component will automatically be rendered.

Why is this so simple?

Next.js uses a declarative pages structure, which is based on the filesystem structure.

Pages are inside a pages folder, and the page URL is determined by the page file name. The filesystem is the pages API.

Server-side rendering

Open the page source, View -> Developer -> View Source with Chrome.

As you can see, the HTML generated by the component is sent directly in the page source. It’s not rendered client-side, but instead it’s rendered on the server.

The Next.js team wanted to create a developer experience for server rendered pages similar to the one you get when creating a basic PHP project, for example, where you drop PHP files and you call them, and they show up as pages. Internally of course it’s all very different, but the apparent ease of use is clear.

Add a second page

Let’s create another page, in pages/contact.js

export default () => (
  <div>
    <p>
      <a href='mailto:[email protected]'>Contact us!</a>
    </p>
  </div>
)

If you point your browser to localhost:3000/contact this page will be rendered. As you can see, also this page is server rendered.

Hot reloading

Note how you did not have to restart the npm process to load the second page. Next.js does this for you under the hood.

Client rendering

Server rendering is very convenient in your first page load, for all the reasons we saw above, but when it comes to navigating inside the website, client-side rendering is key to speeding up the page load and improving the user experience.

Next.js provides a Link component you can use to build links. Try linking the two pages above.

Change index.js to this code:

import Link from 'next/link'

export default () => (
  <div>
    <p>Hello World!</p>
    <Link href='/contact'>
      <a>Contact me!</a>
    </Link>
  </div>
)

Now go back to the browser and try this link. As you can see, the Contact page loads immediately, without a page refresh.

This is client-side navigation working correctly, with complete support for the History API, which means your users back button won’t break.

If you now cmd-click the link, the same Contact page will open in a new tab, now server rendered.

Dynamic pages

A good use case for Next.js is a blog, as it’s something that all developers know how it works, and it’s a good fit for a simple example of how to handle dynamic pages.

A dynamic page is a page that has no fixed content, but instead display some data based on some parameters.

Change index.js to

import Link from 'next/link'

const Post = (props) => (
  <li>
    <Link href={`/post?title=${props.title}`}>
      <a>{props.title}</a>
    </Link>
  </li>
)

export default () => (
  <div>
    <h2>My blog</h2>
    <ul>
      <li>
        <Post title='Yet another post' />
        <Post title='Second post' />
        <Post title='Hello, world!' />
      </li>
    </ul>
  </div>
)

This will create a series of posts and will fill the title query parameter with the post title:

The posts list

Now create a post.js file in the pages folder, and add:

export default (props) => <h1>{props.url.query.title}</h1>

Now clicking a single post will render the post title in a h1 tag:

A single post

You can use clean URLs without query parameters. The Next.js Link component helps us by accepting an as attribute, which you can use to pass a slug:

import Link from 'next/link'

const Post = (props) => (
  <li>
    <Link as={`/${props.slug}`} href={`/post?title=${props.title}`}>
      <a>{props.title}</a>
    </Link>
  </li>
)

export default () => (
  <div>
    <h2>My blog</h2>
    <ul>
      <li>
        <Post slug='yet-another-post' title='Yet another post' />
        <Post slug='second-post' title='Second post' />
        <Post slug='hello-world' title='Hello, world!' />
      </li>
    </ul>
  </div>
)

CSS-in-JS

Next.js by default provides support for styled-jsx, which is a CSS-in-JS solution provided by the same development team, but you can use whatever library you prefer, like Tailwind CSS.

Example:

export default () => (
  <div>
    <p>
      <a href='mailto:[email protected]'>Contact us!</a>
    </p>
    <style jsx>{`
      p {
        font-family: 'Courier New';
      }
      a {
        text-decoration: none;
        color: black;
      }
      a:hover {
        opacity: 0.8;
      }
    `}</style>
  </div>
)

Styles are scoped to the component, but you can also edit global styles adding global to the style element:

export default () => (
  <div>
    <p>
      <a href='mailto:[email protected]'>Contact us!</a>
    </p>
    <style jsx global>{`
      body {
        font-family: 'Benton Sans', 'Helvetica Neue';
        margin: 2em;
      }
      h2 {
        font-style: italic;
        color: #373fff;
      }
    `}</style>
  </div>
)

Exporting a static site

A Next.js application can be easily exported as a static site, which can be deployed on one of the super fast static site hosts, like Vercel (which is made by the same team that works on Next.js), but also Netlify, Cloudflare Pages or Firebase Hosting, without the need to set up a Node environment.

Deploying to Vercel

If your site is more than a static site, and you use API routes, I highly recommend using Vercel.

Vercel is the company behind Next.js and provides this awesome hosting service tailored for Next.js applications.

Vercel is a great hosting for any website, but in particular when it comes to Next.js you can use the API routes as serverless functions.

While other platforms like Netlify have a way to deploy Next.js apps that have API routes (see https://docs.netlify.com/integrations/frameworks/next-js/), on Vercel Next.js is a first-class citizen.

You have API routes that are deployed as serverless functions automatically and you can have more granular control, see logs, etc.

You must check it out if you use Next.js

Read more on Next.js

Check out my free Next.js Handbook!

Are you intimidated by Git? Can’t figure out merge vs rebase? Are you afraid of screwing up something any time you have to do something in Git? Do you rely on ChatGPT or random people’s answer on StackOverflow to fix your problems? Your coworkers are tired of explaining Git to you all the time? Git is something we all need to use, but few of us really master it. I created this course to improve your Git (and GitHub) knowledge at a radical level. A course that helps you feel less frustrated with Git. Launching May 21, 2024. Join the waiting list!

Here is how can I help you: