nested routes with react router v4 / v5

  • Last Update :
  • Techknowledgy :

allows to use both nested routes (like in v3) and separate, splitted routes (v4, v5). Keep all routes in one place for small/medium size apps: I succeeded in defining nested routes by wrapping with Switch and define nested route before than root route.

This is our Splunktool team suggestion ✌, we tried and its working fine
<NavLink
              className={({ isActive }) =>
                isActive ? "active nav-link" : "nav-link"
              }
              to="/admin/new"
            >
              Add New
            </NavLink>
          </li>
          <li class="nav-item">
            <NavLink
              className={({ isActive }) =>
                isActive ? "active nav-link" : "nav-link"
              }
              to="/admin"
              end={true}
            >
              Manage
            </NavLink>

For instance

<Route path='/topics' component={Topics}>
  <Route path='/topics/:topicId' component={Topic} />
</Route>

should become

<Route path='/topics' component={Topics} />

It's true that in order to nest Routes you need to place them in the child component of the Route.

However if you prefer a more inline syntax rather than breaking your Routes up across components, you can provide a functional component to the render prop of the Route you want to nest under.

<BrowserRouter>

  <Route path="/" component={Frontpage} exact />
  <Route path="/home" component={HomePage} />
  <Route path="/about" component={AboutPage} />

  <Route
    path="/admin"
    render={({ match: { url } }) => (
      <>
        <Route path={`${url}/`} component={Backend} exact />
        <Route path={`${url}/home`} component={Dashboard} />
        <Route path={`${url}/users`} component={UserPage} />
      </>
    )}
  />

</BrowserRouter>

Suggestion : 2

Notice where we're linking to, /topics/${id}. If we're going to link someone to /topics/${id}, that means we need to render a Route which is going to match at that path. This is the first big concept of nested routes with React Router v5 - it doesn't matter if you render a Route in your main component or in a child component, if the path matches the app's location, the children element will be rendered. Finally, we have a nice foundation to start talking about how we go about dealing with nested routes with React Router v5. Thanks for your patience 🤝. This is why understanding Route was so important. The mental model for Route is still the exact same, but for some reason your brain gets all worked up the first time you render a Route outside of the main App component.

1._
<Route path="/home">
   <Dashboard />
</Route>
2._
const topics = [{
   name: "React Router",
   id: "react-router",
   description: "Declarative, component based routing for React",
   resources: [{
      name: "URL Parameters",
      id: "url-parameters",
      description: "URL parameters are parameters whose values are set dynamically in a page's URL. This allows a route to render the same component while passing that component the dynamic portion of the URL so it can change based off of it.",
      url: "https://ui.dev/react-router-v5-url-parameters/",
   }, {
      name: "Programmatically navigate",
      id: "programmatically-navigate",
      description: "When building an app with React Router, eventually you'll run into the question of navigating programmatically. The goal of this post is to break down the correct approaches to programmatically navigating with React Router.",
      url: "https://ui.dev/react-router-v5-programmatically-navigate/",
   }, ],
}, {
   name: "React.js",
   id: "reactjs",
   description: "A JavaScript library for building user interfaces",
   resources: [{
      name: "React Lifecycle Events",
      id: "react-lifecycle",
      description: "React Lifecycle events allow you to tie into specific phases of a component's life cycle",
      url: "https://ui.dev/an-introduction-to-life-cycle-events-in-react-js/",
   }, {
      name: "React AHA Moments",
      id: "react-aha",
      description: "A collection of 'Aha' moments while learning React.",
      url: "https://ui.dev/react-aha-moments/",
   }, ],
}, {
   name: "Functional Programming",
   id: "functional-programming",
   description: "In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.",
   resources: [{
      name: "Imperative vs Declarative programming",
      id: "imperative-declarative",
      description: "A guide to understanding the difference between Imperative and Declarative programming.",
      url: "https://ui.dev/imperative-vs-declarative-programming/",
   }, {
      name: "Building User Interfaces with Pure Functions and Function Composition",
      id: "fn-composition",
      description: "A guide to building UI with pure functions and function composition in React",
      url: "https://ui.dev/building-user-interfaces-with-pure-functions-and-function-composition-in-react-js/",
   }, ],
}, ];
3._
import * as React from "react";import {  BrowserRouter as Router,  Link,  Route, // for later} from "react-router-dom";
const topics = [  // ...];
export default function App() {  return (    <Router>      <div style={{ width: 1000, margin: "0 auto" }}>        <ul>          <li>            <Link to="/">Home</Link>          </li>          <li>            <Link to="/topics">Topics</Link>          </li>        </ul>      </div>    </Router>  );}
5._
export default function App() {  return (    <Router>      <div style={{ width: 1000, margin: "0 auto" }}>        <ul>          <li>            <Link to="/">Home</Link>          </li>          <li>            <Link to="/topics">Topics</Link>          </li>        </ul>
        <hr />
        <Route exact path="/">          <Home />        </Route>        <Route path="/topics">          <Topics />        </Route>      </div>    </Router>  );}
6._
function Topics() {  return (    <div>      <h1>Topics</h1>      <ul>        {topics.map(({ name, id }) => (          <li key={id}>            <Link to={`/topics/${id}`}>{name}</Link>          </li>        ))}      </ul>    </div>  );}

Suggestion : 3

Route allows you to map URL paths to different React components. For example, say we wanted to render the Dashboard component whenever a user navigates to the /home path. To do that, we'd render a Route that looks like this. Now what we want to do is render a few Routes so that we can map different components to the application's path. However, before we can do that, we need to actually build out those components. As you saw earlier in the final version of our app, the two top-level components we'll need are Home and Topics. For now, we'll throw some placeholder text in both of them. Now that we have our two top-level components, we need to create a Route for each of them. Home will be rendered when the user is at / and Topics will be rendered when the user is at /topics.

1._
<Route path="/home" component={Dashboard} />
2._
const topics = [{
   name: "React Router",
   id: "react-router",
   description: "Declarative, component based routing for React",
   resources: [{
      name: "URL Parameters",
      id: "url-parameters",
      description: "URL parameters are parameters whose values are set dynamically in a page's URL. This allows a route to render the same component while passing that component the dynamic portion of the URL so it can change based off of it.",
      url: "https://ui.dev/react-router-url-parameters/",
   }, {
      name: "Programmatically navigate",
      id: "programmatically-navigate",
      description: "When building an app with React Router, eventually you'll run into the question of navigating programmatically. The goal of this post is to break down the correct approaches to programmatically navigating with React Router.",
      url: "https://ui.dev/react-router-programmatically-navigate/",
   }, ],
}, {
   name: "React.js",
   id: "reactjs",
   description: "A JavaScript library for building user interfaces",
   resources: [{
      name: "React Lifecycle Events",
      id: "react-lifecycle",
      description: "React Lifecycle events allow you to tie into specific phases of a component's life cycle",
      url: "https://ui.dev/an-introduction-to-life-cycle-events-in-react-js/",
   }, {
      name: "React AHA Moments",
      id: "react-aha",
      description: "A collection of 'Aha' moments while learning React.",
      url: "https://ui.dev/react-aha-moments/",
   }, ],
}, {
   name: "Functional Programming",
   id: "functional-programming",
   description: "In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.",
   resources: [{
      name: "Imperative vs Declarative programming",
      id: "imperative-declarative",
      description: "A guide to understanding the difference between Imperative and Declarative programming.",
      url: "https://ui.dev/imperative-vs-declarative-programming/",
   }, {
      name: "Building User Interfaces with Pure Functions and Function Composition",
      id: "fn-composition",
      description: "A guide to building UI with pure functions and function composition in React",
      url: "https://ui.dev/building-user-interfaces-with-pure-functions-and-function-composition-in-react-js/",
   }, ],
}, ];
3._
import React from "react";import {  BrowserRouter as Router,  Link,  Route, // for later} from "react-router-dom";
const topics = [  // ...];
class App extends React.Component {  render() {    return (      <Router>        <div style={{ width: 1000, margin: "0 auto" }}>          <ul>            <li>              <Link to="/">Home</Link>            </li>            <li>              <Link to="/topics">Topics</Link>            </li>          </ul>        </div>      </Router>    );  }}
export default App;
5._
class App extends React.Component {  render() {    return (      <Router>        <div style={{ width: 1000, margin: "0 auto" }}>          <ul>            <li>              <Link to="/">Home</Link>            </li>            <li>              <Link to="/topics">Topics</Link>            </li>          </ul>
          <hr />
          <Route exact path="/" component={Home} />          <Route path="/topics" component={Topics} />        </div>      </Router>    );  }}
6._
function Topics() {  return (    <div>      <h1>Topics</h1>      <ul>        {topics.map(({ name, id }) => (          <li key={id}>            <Link to={`/topics/${id}`}>{name}</Link>          </li>        ))}      </ul>    </div>  );}

Suggestion : 4

render: handy for inline rendering. The render prop expects a function that returns an element when the location matches the route’s path. children: this is similar to render, in that it expects a function that returns a React component. However, children gets rendered regardless of whether the path is matched with the location or not. Note that when using the component prop to render a route, the match, location and history route props are implicitly passed to the component. When using the newer route rendering pattern, this is not the case.

Routing is the process of keeping the browser URL in sync with what’s being rendered on the page. React Router lets you handle routing declaratively. The declarative routing approach allows you to control the data flow in your application, by saying “the route should look like this”:

<Route path="/about">
   <About />
</Route>

Node comes bundled with npm, a package manager for JavaScript, with which we’re going to install some of the libraries we’ll be using. You can learn more about using npm here.

You can check that both are installed correctly by issuing the following commands from the command line:

node - v >
   12.19 .0

npm - v >
   6.14 .8

With that done, let’s start off by creating a new React project with the Create React App tool. You can either install this globally, or use npx, like so:

npx create - react - app react - router - demo

The React Router library comprises three packages: react-router, react-router-dom, and react-router-native. The core package for the router is react-router, whereas the other two are environment specific. You should use react-router-dom if you’re building a website, and react-router-native if you’re in a mobile app development environment using React Native.

Use npm to install react-router-dom:

npm install react - router - dom

Then start the development server with this:

npm run start

Suggestion : 5

It's true that in order to nest Routes you need to place them in the child component of the Route. However if you prefer a more inline syntax rather than breaking your Routes up across components, you can provide a functional component to the render prop of the Route you want to nest under. If you're interested in why the render prop should be used, and not the component prop, it's because it stops the inline functional component from being remounted on every render. See the documentation for more detail. In react-router-v4 you don't nest <Routes />. Instead, you put them inside another <Component />.

A frontend and an admin area.

I was thinking about something like this:

<Match pattern="/" component={Frontpage}>
  <Match pattern="/home" component={HomePage} />
  <Match pattern="/about" component={AboutPage} />
</Match>
<Match pattern="/admin" component={Backend}>
  <Match pattern="/home" component={Dashboard} />
  <Match pattern="/users" component={UserPage} />
</Match>
<Miss component={NotFoundPage} />

Final solution:

This is the final solution I am using right now. This example also has a global error component like a traditional 404 page.

import React, { Component } from 'react';
import { Switch, Route, Redirect, Link } from 'react-router-dom';

const Home = () => <div><h1>Home</h1></div>;
const User = () => <div><h1>User</h1></div>;
const Error = () => <div><h1>Error</h1></div>

const Frontend = props => {
  console.log('Frontend');
  return (
    <div>
      <h2>Frontend</h2>
      <p><Link to="/">Root</Link></p>
      <p><Link to="/user">User</Link></p>
      <p><Link to="/admin">Backend</Link></p>
      <p><Link to="/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
      <Switch>
        <Route exact path='/' component={Home}/>
        <Route path='/user' component={User}/>
        <Redirect to={{
          state: { error: true }
        }} />
      </Switch>
      <footer>Bottom</footer>
    </div>
  );
}

const Backend = props => {
  console.log('Backend');
  return (
    <div>
      <h2>Backend</h2>
      <p><Link to="/admin">Root</Link></p>
      <p><Link to="/admin/user">User</Link></p>
      <p><Link to="/">Frontend</Link></p>
      <p><Link to="/admin/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
      <Switch>
        <Route exact path='/admin' component={Home}/>
        <Route path='/admin/user' component={User}/>
        <Redirect to={{
          state: { error: true }
        }} />
      </Switch>
      <footer>Bottom</footer>
    </div>
  );
}

class GlobalErrorSwitch extends Component {
  previousLocation = this.props.location

  componentWillUpdate(nextProps) {
    const { location } = this.props;

    if (nextProps.history.action !== 'POP'
      && (!location.state || !location.state.error)) {
        this.previousLocation = this.props.location
    };
  }

  render() {
    const { location } = this.props;
    const isError = !!(
      location.state &&
      location.state.error &&
      this.previousLocation !== location // not initial render
    )

    return (
      <div>
        {          
          isError
          ? <Route component={Error} />
          : <Switch location={isError ? this.previousLocation : location}>
              <Route path="/admin" component={Backend} />
              <Route path="/" component={Frontend} />
            </Switch>}
      </div>
    )
  }
}

class App extends Component {
  render() {
    return <Route component={GlobalErrorSwitch} />
  }
}

export default App;

For instance

<Route path='/topics' component={Topics}>
  <Route path='/topics/:topicId' component={Topic} />
</Route>

with

const Topics = ({ match }) => (
  <div>
    <h2>Topics</h2>
    <Link to={`${match.url}/exampleTopicId`}>
      Example topic
    </Link>
    <Route path={`${match.path}/:topicId`} component={Topic}/>
  </div>
) 
6._
<BrowserRouter>

  <Route path="/" component={Frontpage} exact />
  <Route path="/home" component={HomePage} />
  <Route path="/about" component={AboutPage} />

  <Route
    path="/admin"
    render={({ match: { url } }) => (
      <>
        <Route path={`${url}/`} component={Backend} exact />
        <Route path={`${url}/home`} component={Dashboard} />
        <Route path={`${url}/users`} component={UserPage} />
      </>
    )}
  />

</BrowserRouter>