Up to this point, we have been working with routers that are small enough to be rendered entirely in a single file. But as an application grows in scope, it can be useful to split up the router and write Routes nearer to where the related UI logic is written.

Let’s return to our tech article website example, and imagine that the engineering team is building out a Categories feature that will organize tech news articles by their category – front end, back end, mobile development, etc. In addition to a Categories component (which will render links to each individual category), the team has created a Category view that will display all the articles for a given category.

Previously, we might have written a router like this:

// In the top level App component <Router> <Route path={'/categories/:categoryName'}> <Category /> </Route> <Route path={'/categories'}> <Categories /> </Route> {/* Other Routes */} </Router>

There’s nothing wrong with this way of routing, but as soon as you start to introduce more features into your application, you may find that having all the routes contained in a single router becomes a bit unwieldy. The way around this is to get comfortable with rendering routes from components nested within your app.

For example, consider the Categories component, which iterates through a list of categories and creates Links for each category:

function Categories ({ categories }) { return ( <ul> { categories.map(category => <li> <Link to={`/categories/${category}`}> {category} </Link> </li> ) } </ul> ); };

Clicking on a link rendered by this component will cause the URL to change, for example, to /categories/html. According to our previously defined Router, the route '/categories/:categoryName' will then match and the Category component will render.

Notice that the code for the Categories component doesn’t indicate which component will be rendered when the user clicks on one of the category links (it’s the Category component). We have to navigate back to the top-level App component file where the Router is defined in order to see that the Category component will be rendered when the URL changes to /categories/html. This separation of cause and effect is not ideal.

Because React Router handles routing dynamically (eg. routes exist when they are rendered), you can render a Route anywhere within your application. In this case, we can relocate the Route that renders an individual Category component to within the Categories component where the links to that route are defined:

import { Link, Route } from 'react-router-dom' function Categories ({ categories }) { return ( <div> <ul> { categories.map(category => <li> <Link to={`/categories/${category}`}> {category} </Link> </li> ) } </ul> <Route path={'/categories/:categoryName'}> <Category /> </Route> </div> ) }

As a result, the top-level router can be simplified:

// In the top level App component <Router> {/* The Category route has been removed. */} <Route path={'/categories'}> <Categories /> </Route> {/* Other Routes */} </Router>

Rewriting your routes this way makes it very obvious what will happen when the user clicks on a link. It also allows us to clean up our top-level router by removing the route for an individual category. Splitting routes up this way also makes an application more efficient since Routes are not always rendered. Instead, Routes are only rendered when the UI logic requires them to be.


Task 1

In the running application, navigate to the sign-up form and choose a username. Then navigate to the new “Profile” link that will appear. The URL will change to /profile and you should see the username you just entered, followed by a link to an “Edit” page. Try clicking on this link – you’ll notice that the URL changes, but the page does not.

The EditProfileForm component should render when the URL changes to /profile/edit but it is currently not being rendered by the application. Let’s fix that with a nested routing approach.

First, open up Profile.js which can be found in the src/components/ folder.

Task 2

Notice that the EditProfileForm component is being imported into Profile.js but isn’t being used.

At the bottom of the <main> element returned by Profile, render EditProfileForm when the URL path matches /profile/edit.

To test your code, try clicking on the “Edit” button.


Notice that the EditProfileForm component is rendered alongside the Profile component, but only when the URL matches the path /profile/edit. Using a nested approach, we can choose to render both of these components at the same time for this particular URL.

If EditProfileForm were rendered in the App component’s Switch component, then only one would be rendered at a time.

Take this course for free

Mini Info Outline Icon
By signing up for Codecademy, you agree to Codecademy's Terms of Service & Privacy Policy.

Or sign up using:

Already have an account?