Getting started with React Router v4

getting-started-with-react-router-v4-header.png

In this article, learn some of most important concepts in React Router by building a portfolio website that uses the library to navigate through its pages.

Introduction

This tutorial will take you through some of the most important concepts in React Router by showing how to build a simple portfolio website that uses the library to navigate through its pages. Single Page Apps (SPAs) are all the rave these days. Unlike a traditional web application, an SPA dynamically rewrites the current page instead of requesting entirely new pages from the server. This makes web apps behave more like a desktop or mobile applications.

In order to make this work, a way to distinguish between each page of the application is needed and this is typically achieved by using a router. In the React ecosystem, React Router is the most popular choice for implementing routing.

What we’ll be building

You can view the completed version of the site here. The code used in this tutorial can be found on GitHub.

Before you continue, note that this tutorial assumes prior experience with writing React applications.

Installation and setup

To get started, we will install create-react-app for bootstrapping our application.

1npm install -g create-react-app

If you’re not familiar with create-react-app, it is a quick and easy way to get started with building React applications without messing around with configuring build tools such as Webpack and Babel. You can find out more on its GitHub repository.

Next, create a new app, change into the app’s directory and start the app in development mode.

1create-react-app portfolio
2    cd portfolio
3    npm start

You can open http://localhost:3000 to preview it in the browser.

Next, we need to install React Router. In version 4, the library was broken down into 2 packages: react-router-dom and react-router-native.

Since we’re building this app for the browser, react-router-dom is what we will install and import from. If you’re working with React Native, you’ll want to use react-router-native instead.

1npm install --save react-router-dom

Choosing a router

When working in a browser environment, React Router provides two Routers for your consumption: BrowserRouter and HashRouter. You need to decide which one to use when starting your project and your choice will mostly depend on your server architecture.

BrowserRouter is the recommended default but it may require additional server configuration to handle dynamic requests. HashRouter is not suitable for all situations, but it can be a good solution for static websites.

For this tutorial, we will use BrowserRouter as our Router of choice.

Change to the src folder inside the portfolio directory, open up the index.js file contained therein and import BrowserRouter into scope below the other imports:

1import { BrowserRouter } from 'react-router-dom';

You can only pass a single child element to a Router, so it is necessary to create a root component that renders the rest of your application and then pass that in as the child of the Router.

create-react-app already provides an App component for this purpose. We need to place the App component inside the BrowserRouter so that routing can work for the entire application.

Change your index.js file to look like this:

1import React from 'react';
2    import ReactDOM from 'react-dom';
3    import App from './App';
4    import { BrowserRouter } from 'react-router-dom';
5
6    ReactDOM.render((
7    <BrowserRouter>
8      <App />
9    </BrowserRouter>
10    ), document.getElementById('root'));

That’s it. We can now concentrate on building up our app.

Adding the styles

Before we start implementing the routes, open up App.css and replace its contents with the following styles:

1html {
2      box-sizing: border-box;
3    }
4
5    *, *::before, *::after {
6      box-sizing: inherit;
7      padding: 0;
8      margin: 0;
9    }
10
11    .app {
12      width: 960px;
13      margin: 0 auto;
14      padding: 20px;
15    }
16
17    nav ul {
18      list-style: none;
19      display: flex;
20      background-color: black;
21      margin-bottom: 20px;
22    }
23
24    nav ul li {
25      padding: 20px;
26    }
27
28    nav ul li a {
29      color: white;
30      text-decoration: none;
31    }
32
33    .current {
34      border-bottom: 4px solid white;
35    }
36
37    h1 {
38      margin-bottom: 20px;
39    }
40
41    p {
42      margin-bottom: 15px;
43    }

Application structure

Open up App.js in your editor and change it to look like this:

1import React from 'react';
2    import './App.css';
3
4    const App = () => (
5      <div className='app'>
6        <h1>React Router Demo</h1>
7        <Navigation />
8        <Main />
9      </div>
10    );
11
12    export default App;

The App component references two other components: Navigation which will display the navigation menu, and Main which will display the contents of each view.

On a real project, you will want to create a new file for each component but, to simplify things here, we will put all the other components into the App.js file.

Creating the navigation menu

The way you specify navigation links in React Router is not by using the anchor tag and passing in the path via its href attribute as you’re probably used to.

Instead we use the NavLink component which was created for this purpose. NavLink helps us conditionally add styling attributes to the rendered element if it matches the current URL. It is a special version of the Link component which no longer does conditional styling as of version 4.

Import NavLink into scope first:

1import { NavLink } from 'react-router-dom';

Then create the Navigation component just below the App component.

1const Navigation = () => (
2      <nav>
3        <ul>
4          <li><NavLink to='/'>Home</NavLink></li>
5          <li><NavLink to='/about'>About</NavLink></li>
6          <li><NavLink to='/contact'>Contact</NavLink></li>
7        </ul>
8      </nav>
9    );

Creating routes

To create a route, we will make use of the Route component whose basic responsibility is to render some component when a URL matches the Route‘s path.

A route can be placed anywhere inside the Router. The Route component will render to wherever it was written if the route matches.

You can also group multiple Routes inside a Switch component to render only the first child Route that matches the current URL.

Go ahead and import the two components into scope:

1import { NavLink, Switch, Route } from 'react-router-dom';

Next, create the Main component just below Navigation:

1const Main = () => (
2      <Switch>
3        <Route path='/' component={Home}></Route>
4        <Route path='/about' component={About}></Route>
5        <Route path='/contact' component={Contact}></Route>
6      </Switch>
7    );

Here, we have created three routes — one for each view of our application.

The Route component expects a path prop that describes the path that the route matches. If you do not specify a path, the route will always match for any URL.

The component prop is used to specify what component should be rendered when a route matches. There are two other props that can be used for this purpose as well: render and children. See the relevant documentation to understand the situations when either option is useful.

Creating the views

Let’s go ahead and create each of the components that hold the views for the site. Add the following chunk of code just above the Main component.

1const Home = () => (
2      <div className='home'>
3        <h1>Welcome to my portfolio website</h1>
4        <p> Feel free to browse around and learn more about me.</p>
5      </div>
6    );
7
8    const About = () => (
9      <div className='about'>
10        <h1>About Me</h1>
11        <p>Ipsum dolor dolorem consectetur est velit fugiat. Dolorem provident corporis fuga saepe distinctio ipsam? Et quos harum excepturi dolorum molestias?</p>
12        <p>Ipsum dolor dolorem consectetur est velit fugiat. Dolorem provident corporis fuga saepe distinctio ipsam? Et quos harum excepturi dolorum molestias?</p>
13      </div>
14    );
15
16    const Contact = () => (
17      <div className='contact'>
18        <h1>Contact Me</h1>
19        <p>You can reach me via email: <strong>hello@example.com</strong></p>
20      </div>
21    );

All our view components simply return some JSX that will be rendered into the DOM when the routes for each component match the current URL.

If you preview the app now and click on the navigation links, you will notice that the Home component is rendered for all routes.

This is because a Route will match for any URL that contains its path by default. This is why / matches for both /``about and /contact. While this is the default behavior at the moment, it may change in future releases.

To have our routes render only when the path is an exact match on the URL, the exact prop is required on each of our routes. We don’t need to pass in anything to exact as it is a Boolean.

1const Main = () => (
2      <Switch>
3        <Route exact path='/' component={Home}></Route>
4        <Route exact path='/about' component={About}></Route>
5        <Route exact path='/contact' component={Contact}></Route>
6      </Switch>
7    );

The exact prop helps to specify that we want to match the exact path that is provided in the path prop. By adding this prop, we make sure each Route is rendered only if an exact match is made.

Preview the app again and click each of the navigation links. You will see that the routes now work perfectly.

Highlight the current view

Our final task is to indicate the current view in the application on the navigation bar.

We can do this in two ways: by applying a set of styles to a NavLink via its activeStyle prop so that the style takes effect when it is active, or by setting a class on the element when it is active using the activeClassName prop. You can then create the styles for that class in your CSS.

We will use the latter method to complete this task.

By default, the class name ‘active’ is applied to the active NavLink without any need to specify the activeClassName prop. If you’re OK with this, you can go ahead and create styles for that class in your CSS and everything should work just fine.

If you want to change the class applied to active NavLinks, you can do so by setting the activeClassName prop on the NavLink instances and pass in the name of the class you prefer.

We also need to use exact on the NavLinks as well for the same reasons stated earlier.

1const Navigation = () => (
2      <nav>
3        <ul>
4          <li><NavLink exact activeClassName="current" to='/'>Home</NavLink></li>
5          <li><NavLink exact activeClassName="current" to='/about'>About</NavLink></li>
6          <li><NavLink exact activeClassName="current" to='/contact'>Contact</NavLink></li>
7        </ul>
8      </nav>
9    );

Here we set the activeClassName to current. In our CSS, a rule already exists for this class:

1.current {
2      border-bottom: 4px solid white;
3    }

Now, if you preview the app again, you will see that a white border is applied to the bottom of the currently active link.

View the demo – http://react-router-demo.surge.sh/

Wrap up

I hope this tutorial has helped you learn how you can use React Router to build your own SPAs. We successfully covered some of its major features in this article but keep in mind that these are only a subset of what the library provides.

The React Router Documentation provides excellent information about all components included in the library as well as working examples to get you started so make sure to check that out!