developers

Next.js Practical Introduction: Navigation and Routing

Learn how to use Next.js to add navigation and routing to server-side rendered React applications.

Jul 30, 20198 min read

In this tutorial series, you'll be provided with a practical introduction to how Next.js can help you build web applications.

Created by ZEIT, a cloud company for hosting frontends and serverless functions, Next.js is a React framework capable of creating applications that run both on the client and the server, otherwise known as Universal JavaScript applications. This framework helps you build universal apps faster by streamlining basic features like client-side routing and page layout while simplifying advance features like server-side rendering and code splitting.

Part one of this Nextjs series shows developers how to create pages and layouts. In the second part of the series, you learned how to add styling and create themes. In this third part of the series, you'll learn how to navigate to pages and implement routing using using Next.js 9, the most recent framework version at the time of writing. Familiarity with the React library is recommended.

Setting Up the Project

If you are starting from this part, you can get the app that was built in the previous, second part by cloning the repo and checking out the

part-2
branch:

git clone git@github.com:auth0-blog/whatabyte-next-intro.git whatabyte
cd whatabyte
git checkout part-2

If you haven't connected to GitHub using SSH, please clone the repo with the HTTPS URL:

git clone https://github.com/auth0-blog/whatabyte-next-intro.git whatabyte
cd whatabyte
git checkout part-2

The repository is cloned into the directory called

whatabyte
on your local machine. If the directory doesn't exist, it is created.

With the branch checked out, proceed to install the project dependencies:

npm install

Finally, execute the following command in your terminal to run the app:

npm run dev

To see the app in action and start following the tutorial, visit

http://localhost:3000
on your browser.

Understanding Next.js Routing

The WHATABYTE app has four core routes:

  • /explore
    : To show trending restaurants and dishes
  • /nearme
    : To show restaurants near the user
  • /mycart
    : To show the orders of the user
  • /profile
    : To show the user information such as name, address, and billing data

From these four core routes, the

/explore
route will be the main route. Thus, the root path,
/
, should redirect to
/explore
.

First, to get familiar with navigation in Next.js, you'll navigate to the Explore page from the Index page by adding a hyperlink inside the content container of the Index page. You can do this easily by using the Next.js

Link
component inside
index.js
as follows:

// pages/index.js
import Link from "next/link";

import Layout from "../components/Layout";

const Index = () => (
  <Layout>
    <br />
    <Link href="/explore">
      <a> Welcome to WHATABYTE! Start Exploring Now</a>
    </Link>
  </Layout>
);

export default Index;

The Next.js

Link
component takes a route or navigation path as the value of its
href
property. You can embed any other component within
Link
as long as that component can take an
href
prop.

After the view updates in the browser, visit

http://localhost:3000/
and click on the link. It will take you to the Explore page.

Next.js app using the Link component

Using this link to navigate to another page is good for testing but what the app needs is navigation through the

NavBar
component. To do so, you'd need a
NavButton
component to make the creation of four navigation buttons standard and easy to maintain.

To make the buttons look nice, you'll use the FontAwesome icons. Using another console window or tab, install the following packages:

npm i @fortawesome/fontawesome-svg-core \
  @fortawesome/free-solid-svg-icons \
  @fortawesome/react-fontawesome
  • Windows PowerShell:
npm i @fortawesome/fontawesome-svg-core `
  @fortawesome/free-solid-svg-icons `
  @fortawesome/react-fontawesome

Creating the navigation bar dynamically would be much easier if you can encapsulate all of its configuration in a file. Under the

whatabyte
directory, create a
config
subdirectory:

mkdir config

Within that new subdirectory, create a

buttons.js
file within it:

touch config/buttons.js
  • Windows PowerShell:
ni config/buttons.js

Then, populate

buttons.js
as follows:

// config/buttons.js

import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
  faCompass,
  faMapMarkerAlt,
  faUser,
  faShoppingCart
} from "@fortawesome/free-solid-svg-icons";

const navButtons = [
  {
    label: "Explore",
    path: "/explore",
    icon: <FontAwesomeIcon icon={faCompass} />
  },
  {
    label: "Near Me",
    path: "/nearme",
    icon: <FontAwesomeIcon icon={faMapMarkerAlt} />
  },
  {
    label: "My Cart",
    path: "/mycart",
    icon: <FontAwesomeIcon icon={faShoppingCart} />
  },
  {
    label: "Profile",
    path: "/profile",
    icon: <FontAwesomeIcon icon={faUser} />
  }
];

export default navButtons;

Then, under the

components
directory, create a
NavButton.js
file that exports the
NavButton
component:

touch components/NavButton.js
  • Windows PowerShell:
ni components/NavButton.js

Then, populate

NavButton.js
with the following:

// components/NavButton.js

import Link from "next/link";

import "./NavButton.scss";

const NavButton = props => (
  <Link href={props.path}>
    <div className="NavButton">
      <div className="Icon">{props.icon}</div>
      <span className="Label">{props.label}</span>
    </div>
  </Link>
);

export default NavButton;

The

NavButton
needs its proper styling. Create
NavButton.scss
as a sibling file:

touch components/NavButton.scss
  • Windows PowerShell:
ni components/NavButton.scss

Then, populate

NavButton.scss
with the following style rules:

// components/NavButton.scss

.NavButton {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: center;

  height: 100%;

  cursor: pointer;

  .Icon {
    font-size: 20px;
  }

  .Label {
    font-size: 12px;
    text-transform: capitalize;
  }
}

Import the button configuration file in the

Layout
component and pass its data as the
navButtons
props to the
NavBar
component:

// components/Layout.js
import Head from "next/head";

import Header from "./Header";
import NavBar from "./NavBar";

import "./Layout.scss";
import "./index.scss";

import navButtons from "../config/buttons";

const Layout = props => {
  const appTitle = `> WHATABYTE`;

  return (
    <div className="Layout">
      <Head>
        <title>WHATABYTE</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charSet="utf-8" />
      </Head>

      <Header appTitle={appTitle} />
      <div className="Content">{props.children}</div>
      <NavBar navButtons={navButtons} />
    </div>
  );
};

export default Layout;

It's better for the

Layout
component to be the one responsible for passing the button configuration to the
NavBar
component as it is in charge of enforcing a consistent structure for each page of the application. If you wanted to keep
Layout
as a presentational component, then you could wrap it into a container component that passes it the button configuration data.

To render the buttons within the

NavBar
component, update the content of the
NavBar.js
file as follows:

// components/NavBar.js

import "./NavBar.scss";
import NavButton from "./NavButton";

const NavBar = props => (
  <div className="NavBar">
    {props.navButtons.map(button => (
      <NavButton
        key={button.path}
        path={button.path}
        label={button.label}
        icon={button.icon}
      />
    ))}
  </div>
);

export default NavBar;

If your app is not running, execute

npm run dev
in the terminal. Otherwise, visit
http://localhost:3000/
directly and then click on the Profile button. You'll be taken to the Profile page. Try using the other navigation buttons.

If the labels and icons of the buttons look too big, refresh the page.

Did you notice a bit of delay when you navigated from page to page? If you look at the terminal window or tab where the server is running, you may see a message from Next.js explaining that the pages need to be compiled when you first load them, causing a small delay:

Note that pages will be compiled when you first load them.

The navigation bar becomes much more functional and expressive by having those buttons with icons and labels on them. However, adding a visual indicator of what view is active by looking at the navbar would further improve the user experience. You'll learn how to do that in the next section by applying the

.active
CSS class to
NavButton
components dynamically.

Learn how to add navigation and routing to Next.js apps.

Tweet This

Using High-Order Components with Next.js

The Next.js

Link
component has two main props:

  • href
    specifies a path inside the
    pages
    directory to use as a route.

  • as
    is used to customize the path rendered in the browser URL bar.

    There's no "active" class prop that we can pass to it. To style it, you need to do so within its underlying components.

The Next.js router keeps track of the active page through a

router.pathname
property. To inject the
pathname
prop in your component, you can use the
withRouter
higher-order component
. Using this prop with conditional styling will let you have a distinctive style for the active page button.

Open

components/NavButton.js
and update its content to this:

// components/NavButton.js

import Link from "next/link";
import { withRouter } from "next/router";

import "./NavButton.scss";

const NavButton = props => (
  <Link href={props.path}>
    <div
      className={`NavButton ${
        props.router.pathname === props.path ? "active" : ""
      }`}
    >
      <div className="Icon">{props.icon}</div>
      <span className="Label">{props.label}</span>
    </div>
  </Link>
);

export default withRouter(NavButton);

If the

path
prop matches the
router.pathname
prop, the
active
class is added to the
NavButton
class name. Save your changes and test the navigation buttons in the browser. Once you click on a button, its icon lights up into an orange-red-like color.

Next.js nav bar with active view indicator

Recap

In this part of the tutorial, you have learned how to use Next.js built-in components to navigate to pages within your app and to represent the state of the router through navigation UI elements.

You've done a lot so far throughout this tutorial and there is a lot more than could be done using Next.js! The last part of this practical introduction to Next.js will teach you how to create a login page and add user authentication to your app using Passport.js and Auth0.

Again, you can reference Part 2 for adding styling and creating themes, here and Part 1 for creating pages and a page layout

Can't wait to learn about Next.js authentication? Feel free to read Bruno's Next.js Authentication Tutorial.

About Auth0

Auth0 by Okta takes a modern approach to customer identity and enables organizations to provide secure access to any application, for any user. Auth0 is a highly customizable platform that is as simple as development teams want, and as flexible as they need. Safeguarding billions of login transactions each month, Auth0 delivers convenience, privacy, and security so customers can focus on innovation. For more information, visit https://auth0.com.