DEV Community

Cover image for React router dom: Nested routes
Tallan Groberg for ITNEXT

Posted on • Updated on

React router dom: Nested routes

In this tutorial, we are going to build a nested route, or commonly called nested routing params.

If you prefer videos this is the youtube video

I consider this tutorial an example of nested routing because it is on the API level. However for the sake of simplicity they are only defined as routing parameters within the scope of the what is declared in the App.js that this tutorial shows how to build.

Who is this tutorial for.

This is for any one that has a list of information being displayed to a user, when a user clicks a specific item in the list, that item will display a new page only showcasing that item.

How it should be used.

I highly recommend doing this tutorial. deleting what you did. checking how far you can get from memory. referencing the tutorial when you get stuck. Deleting the app after you finish until you can do nested routes from memory.

I'm a huge advocate of memorizing how to code vs... reference, copy and paste, move on.

Memorizing how to do basic pieces of functionality will make you a faster dev in the long run.

Why nested routing is useful.

Having the ability to display more information that a user clicks on in a dynamic way like this keeps your web sites more organized and therefore, scalable.

This is also foundational for almost every e-commerce website, so knowing how to do this could mean the difference between you and your dream job as a developer, or the ability to make an online business.

Why this can be hard.

the reason why nested routes are tricky to learn is because you are representing 1 piece of information in two very different ways and they look very similar in the browser.

Prerequisites

knowledge of javascript and react.

basic knowledge of command line to install npm packages.

if you don't have npm installed on your computer, these commands work on mac or pc. you can get find out how here.

a text editor, I'll be using vs-code.

let us get started. Make a new react app.

create-react-app nested-routes-exp
Enter fullscreen mode Exit fullscreen mode

after it installs, cd into the project folder.

cd nested-routes-exp
Enter fullscreen mode Exit fullscreen mode

inside the root of the project directory, on the command-line, install react-router-dom.

npm i react-router-dom
Enter fullscreen mode Exit fullscreen mode

open your project in your text editor of choice. This is how on the command-line with vs-code.

code .
Enter fullscreen mode Exit fullscreen mode

the top section of your package.json file, also in your root directory,you should have a place that says react-router-dom inside the curly brackets for the dependencies section.


{
  "name": "nested-routes-exp",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.4.0",
    "@testing-library/user-event": "^7.2.1",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
// *you should have this below

    "react-router-dom": "^5.1.2",

// * you should have this above. 
    "react-scripts": "3.3.0"
  },

Enter fullscreen mode Exit fullscreen mode

NOTE: DO NOT change this file directly if it isn't there. run npm i react-router-dom on the command line as shown above.

Now that we know that we know react-router-dom is installed, make sure that the whole app has access to this functionality.

to do this...

  1. Open the index.js file in your text editor.

  2. import {BrowserRouter} from "react-router-dom"

  3. BrowserRouter is just a piece of context that you didn't make but imported instead. so wrap your in BrowserRouter so that you can access it down the component tree.

The whole file should look like this.


import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

//import BrowerRouter
import {BrowserRouter} from 'react-router-dom'

ReactDOM.render(

  //sandwich the App with some BrowserRouter bread
    <BrowserRouter>
      <App />
    </BrowserRouter>
, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Enter fullscreen mode Exit fullscreen mode

index.js in the src folder.

To ensure that this worked, open your App.js and we will import the pieces of functionality we want from react-router-dom and console.log them.

we are going to use {Route, Switch } from "react-router-dom"

Route gives us a way to declare components that appear when we type in a url.

Switch gives the ability to customize which components will display. In this case, the list of things we want and then on the click event displaying that item from the list.

The App.js should look something like this.


import React from 'react';
import logo from './logo.svg';
import './App.css';

// add imports 
import {Route, Switch} from 'react-router-dom'


//add props as an argument
function App(props) {

  console.log('route',Route)
 console.log('switch',Switch)

  return (
    <div className="App">

    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

now start this project on a local server.

npm start

Enter fullscreen mode Exit fullscreen mode

if your App.js is like the one above and you inspect the page you should see 2 console logged functions along with a blank white screen.

Alt Text

we know Route and Switch are here in the App.js to make sure we can use them lets make a Home.js and a NoMatch.js as a 404 page to see if we can get them to display separately.

if you are using vscode, you can right-click the src folder and choose to make a new folder called components, then you can make and name the 2 files.

make the skeleton of these two components.

Home.js

import React from 'react';

const Home = (props) => {
  return (
    <div>
      Home
    </div>
  );
};

export default Home;

Enter fullscreen mode Exit fullscreen mode

NoMatch.js

import React from 'react';

const NoMatch = (props) => {
  return (
    <div>
      Error 404
    </div>
  );
};

export default NoMatch;

Enter fullscreen mode Exit fullscreen mode

import these components in your App.js


import Home from "./components/Home"
import NoMatch from "./components/NoMatch"

Enter fullscreen mode Exit fullscreen mode

add these components to the return on the App.js

 return (
    <div className="App">
 //testing if they show up

      <Home />
      <NoMatch />

    </div>
  );

Enter fullscreen mode Exit fullscreen mode

now we want to add Switch to get the functionality we want by adding the inside the return

 return (
    <div className="App">
      {/* add Switch notice that the no match component isn't rendering. */}
      <Switch>

        <Home />
        <NoMatch />

      </Switch>
    </div>
  );

Enter fullscreen mode Exit fullscreen mode

we are only getting one component right now because Switch reads from top to bottom.

lets add a route.

there are two ways to do this, change your home component to look like this.

<Route exact path='/home'> <Home /> </Route>

Enter fullscreen mode Exit fullscreen mode

this allows us to only render the home component when we type into the Url /home. give it a try

Alt Text

and if we don't designate a path we get a page not found.

Alt Text

There are a few ways to define routes, another syntax is component={} prop for the route

<Route path component={NoMatch} />

Enter fullscreen mode Exit fullscreen mode

Now the only time any other component will show up besides the 404 page is when we define that path here in our App.js

Normally this would be your home page, but for demonstration purposes, I wanted to show how you explicitly define them any way you want.

also, render is better than component because you can pass your own props along with render props. rProps for short.

now we are ready to map through a list on the home page.

I will be using the starwars API to demonstrate.

import {useEffect, useState} on the Home.js like so...

import React, {useEffect, useState} from 'react'

Enter fullscreen mode Exit fullscreen mode

this means that we should install and import axios to make life a little easier.

npm i axios

Enter fullscreen mode Exit fullscreen mode

in the Home.js at the top.

import axios from 'axios'

Enter fullscreen mode Exit fullscreen mode

inside the function make an array for the starwars characters

  const [characters, setCharacters] = useState([])

Enter fullscreen mode Exit fullscreen mode

and a useEffect to handle the get request.


useEffect( () => {
    axios.get(`https://swapi.co/api/people`)
      .then(res => {
        console.log(res.data.results)
      })
  }, [])

Enter fullscreen mode Exit fullscreen mode

This should give you 10 objects in the console.log

Set these objects to state by replacing the console.log with setCharacters.

setCaracters(res.data.results)

Enter fullscreen mode Exit fullscreen mode

now display the characters to the browser by mapping them in the return.

Home: 
      {characters.map(character => {
        return <h1>{character.name}</h1>
      })}

Enter fullscreen mode Exit fullscreen mode

cool almost there.

Next objectives

  1. get the characters name to show up in the url by replacing h1 with a Link .

  2. make the link render a Character component.

import {Link} from react-router-dom

add the name link

<Link to={'/' + character.name}>{character.name}</Link>

Enter fullscreen mode Exit fullscreen mode

click a character and you should see there name appear in the URL.

Alt Text

Make a Character.js we are going to need a axios.get useEffect and useState in this component.

We also need useParams from react router dom. so that we can get the names from the url.

here is the skeleton.

import React, {useEffect, useState} from 'react';
import axios from 'axios'
import {useParams} from 'react-router-dom'

const Character = (props) => {
  return (
    <div>
      Character: 

    </div>
  );
};

export default Character;

Enter fullscreen mode Exit fullscreen mode

Now let's make it so that this component shows up when we click a character by declaring that route in the App.js

Add this Route inside the Switch.

<Route exact path='/:name'> <Character /> </Route>

Enter fullscreen mode Exit fullscreen mode

It’s important to understand that if we were to write the path=“/people/:name” it would be an example of a nested Route, since it’s not it’s considered a routing parameter.

Notice the path='/:name' the : makes it so that we can have an id representing the name.

We need access to a thing that react-router-dom provides called match from params
console.log(useParams()) in the Character.js


console.log(useParams())
Enter fullscreen mode Exit fullscreen mode

Walk down the object so that the character's name is just a string and not a key-value pair.

console.log(useParmas().name)
Enter fullscreen mode Exit fullscreen mode

the console.log save that string to a variable so that you can add it to the url.

Note: react will throw an error if you try and call useParams directly in the useEffect. This means you must call useParams outside the useEffect.

const name = useParams().name
Enter fullscreen mode Exit fullscreen mode

Make the useEffect and the useState

const [character, setCharacter] = useState([])


//above the return. 

Enter fullscreen mode Exit fullscreen mode

Since we want a single character, useEffect has to do a couple things.

  1. do a get request for a single character with a search.

  2. add that character to state.

this is what that useEffect looks like.

useEffect( () => {
    axios.get(`https://swapi.co/api/people/? 
     search=${name}`)
      .then(res => {
        setCharacter(res.data.results)
      })
  }, [])

Enter fullscreen mode Exit fullscreen mode

This is how you set up a search of an API by a click event with react-router-dom.

Make the info about the character appear on the screen you can add anything you want but this is what I wanted to show for this tutorial.

Inside the return statement add the extra info you wanted to display.

return (
    <div>
        Character: 
        {character.map(char => {
          return <>
          <h1>{char.name}</h1>
            <p>eye color: {char.eye_color}</p>
            <p>hair color: {char.hair_color}</p>
            <p>birth year: {char.birth_year}</p>
            <p> gender: {char.gender}</p>
          </>
        })}
        <Link to='/home'>back to home</Link>
    </div>
  );

Enter fullscreen mode Exit fullscreen mode

This is my end result.


import React, {useEffect, useState} from 'react';
import axios from 'axios'
import {Link, useParams} from 'react-router-dom'



const Character = (props) => {
  const [character, setCharacter] = useState([])

    const name = useParams().name

  console.log('character',character)
  useEffect( () => {
    axios.get(`https://swapi.co/api/people/?search=${name}`)
      .then(res => {
        setCharacter(res.data.results)
      })
  }, [])


  return (
    <div>
        Character: 
        {character.map(char => {
          return <>
          <h1>{char.name}</h1>
            <p>eye color: {char.eye_color}</p>
            <p>hair color: {char.hair_color}</p>
            <p>birth year: {char.birth_year}</p>
            <p> gender: {char.gender}</p>
          </>
        })}
        <Link to='/home'>back to home</Link>
    </div>
  );
};

export default Character;
Enter fullscreen mode Exit fullscreen mode

That's it! I would suggest playing around and adding upon this tutorial to get the most out of it.

the API is kind of slow but it might be cool to make your own back-end and database and see what you could do.

I hope this tutorial was helpful.

If there is anything you would like me to add or if there are errors to fix, any kind of feedback in general, add them to the comments below.

github

other react tutorials

Thanks so much for checking out my silly write up!!

Top comments (1)

Collapse
 
tallangroberg profile image
Tallan Groberg

I have updated this tutorial to use the newer features of react-router-dom so that it is that much more assessable to learners.