Nested routes with react router v4 / v5

JavascriptReactjsNestedReact RouterReact Router-V4

Javascript Problem Overview


I am currently struggling with nesting routes using react router v4.

The closest example was the route config in the React-Router v4 Documentation.

I want to split my app in 2 different parts.

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} />

The frontend has a different layout and style than the admin area. So within the frontpage the route home, about and so one should be the child routes.

/home should be rendered into the Frontpage component and /admin/home should be rendered within the Backend component.

I tried some variations but I always ended in not hitting /home or /admin/home.

Edit - 19.04.2017

Because this post has a lot of views right now I updated it with the final solution. I hope it helps someone.

Edit - 08.05.2017

Removed old solutions

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;

Javascript Solutions


Solution 1 - Javascript

In react-router-v4 you don't nest <Routes />. Instead, you put them inside another <Component />.


For instance

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

should become

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

with

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

Here is a basic example straight from the react-router documentation.

Solution 2 - Javascript

react-router v6

Update for 2022 - v6 has nested Route components that Just Work™.

This question is about v4/v5, but the best answer now is just use v6 if you can!

See example code in this blog post. If you can't upgrade just yet, however...

react-router v4 & v5

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>

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.

Note the example wraps the nested Routes in a Fragment. Prior to React 16, you can use a container <div> instead.

Solution 3 - Javascript

Just wanted to mention react-router v4 changed radically since this question was posted/answed.

There is no <Match> component any more! <Switch>is to make sure only the first match is rendered. <Redirect> well .. redirects to another route. Use or leave out exact to either in- or exclude a partial match.

See the docs. They are great. https://reacttraining.com/react-router/

Here's an example I hope is useable to answer your question.

<Router>
  <div>
    <Redirect exact from='/' to='/front'/>
    <Route path="/" render={() => {
      return (
        <div>
          <h2>Home menu</h2>
          <Link to="/front">front</Link>
          <Link to="/back">back</Link>
        </div>
      );
    }} />          
    <Route path="/front" render={() => {
      return (
        <div>
        <h2>front menu</h2>
        <Link to="/front/help">help</Link>
        <Link to="/front/about">about</Link>
        </div>
      );
    }} />
    <Route exact path="/front/help" render={() => {
      return <h2>front help</h2>;
    }} />
    <Route exact path="/front/about" render={() => {
      return <h2>front about</h2>;
    }} />
    <Route path="/back" render={() => {
      return (
        <div>
        <h2>back menu</h2>
        <Link to="/back/help">help</Link>
        <Link to="/back/about">about</Link>
        </div>
      );
    }} />
    <Route exact path="/back/help" render={() => {
      return <h2>back help</h2>;
    }} />
    <Route exact path="/back/about" render={() => {
      return <h2>back about</h2>;
    }} />
  </div>
</Router>

Hope it helped, let me know. If this example is not answering your question well enough, tell me and I'll see if I can modify it.

Solution 4 - Javascript

I succeeded in defining nested routes by wrapping with Switch and define nested route before than root route.

<BrowserRouter>
  <Switch>
    <Route path="/staffs/:id/edit" component={StaffEdit} />
    <Route path="/staffs/:id" component={StaffShow} />
    <Route path="/staffs" component={StaffIndex} />
  </Switch>
</BrowserRouter>

Reference: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md

Solution 5 - Javascript

Using Hooks

The latest update with hooks is to use useRouteMatch.

Main routing component

export default function NestingExample() {
  return (
    <Router>
      <Switch>
       <Route path="/topics">
         <Topics />
       </Route>
     </Switch>
    </Router>
  );
}
Child component
function Topics() {
  // The `path` lets us build <Route> paths 
  // while the `url` lets us build relative links.

  let { path, url } = useRouteMatch();

  return (
    <div>
      <h2>Topics</h2>
      <h5>
        <Link to={`${url}/otherpath`}>/topics/otherpath/</Link>
      </h5>
      <ul>
        <li>
          <Link to={`${url}/topic1`}>/topics/topic1/</Link>
        </li>
        <li>
          <Link to={`${url}/topic2`}>/topics/topic2</Link>
        </li>
      </ul>

      // You can then use nested routing inside the child itself
      <Switch>
        <Route exact path={path}>
          <h3>Please select a topic.</h3>
        </Route>
        <Route path={`${path}/:topicId`}>
          <Topic />
        </Route>
        <Route path={`${path}/otherpath`>
          <OtherPath/>
        </Route>
      </Switch>
    </div>
  );
}

Solution 6 - Javascript

Some thing like this.

import React from 'react';
import {
  BrowserRouter as Router, Route, NavLink, Switch, Link
} from 'react-router-dom';

import '../assets/styles/App.css';

const Home = () =>
  <NormalNavLinks>
    <h1>HOME</h1>
  </NormalNavLinks>;
const About = () =>
  <NormalNavLinks>
    <h1>About</h1>
  </NormalNavLinks>;
const Help = () =>
  <NormalNavLinks>
    <h1>Help</h1>
  </NormalNavLinks>;

const AdminHome = () =>
  <AdminNavLinks>
    <h1>root</h1>
  </AdminNavLinks>;

const AdminAbout = () =>
  <AdminNavLinks>
    <h1>Admin about</h1>
  </AdminNavLinks>;

const AdminHelp = () =>
  <AdminNavLinks>
    <h1>Admin Help</h1>
  </AdminNavLinks>;


const AdminNavLinks = (props) => (
  <div>
    <h2>Admin Menu</h2>
    <NavLink exact to="/admin">Admin Home</NavLink>
    <NavLink to="/admin/help">Admin Help</NavLink>
    <NavLink to="/admin/about">Admin About</NavLink>
    <Link to="/">Home</Link>
    {props.children}
  </div>
);

const NormalNavLinks = (props) => (
  <div>
    <h2>Normal Menu</h2>
    <NavLink exact to="/">Home</NavLink>
    <NavLink to="/help">Help</NavLink>
    <NavLink to="/about">About</NavLink>
    <Link to="/admin">Admin</Link>
    {props.children}
  </div>
);

const App = () => (
  <Router>
    <div>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/help" component={Help}/>
        <Route path="/about" component={About}/>

        <Route exact path="/admin" component={AdminHome}/>
        <Route path="/admin/help" component={AdminHelp}/>
        <Route path="/admin/about" component={AdminAbout}/>
      </Switch>

    </div>
  </Router>
);


export default App;

Solution 7 - Javascript

A complete answer for React Router v6 or version 6 just in case needed.

import Dashboard from "./dashboard/Dashboard";
import DashboardDefaultContent from "./dashboard/dashboard-default-content";
import { Route, Routes } from "react-router";
import { useRoutes } from "react-router-dom";

/*Routes is used to be Switch*/
const Router = () => {

  return (
    <Routes>
      <Route path="/" element={<LandingPage />} />
      <Route path="games" element={<Games />} />
      <Route path="game-details/:id" element={<GameDetails />} />
      <Route path="dashboard" element={<Dashboard />}>
        <Route path="/" element={<DashboardDefaultContent />} />
        <Route path="inbox" element={<Inbox />} />
        <Route path="settings-and-privacy" element={<SettingsAndPrivacy />} />
        <Route path="*" element={<NotFound />} />
      </Route>
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
};
export default Router;
import DashboardSidebarNavigation from "./dashboard-sidebar-navigation";
import { Grid } from "@material-ui/core";
import { Outlet } from "react-router";

const Dashboard = () => {
  return (
    <Grid
      container
      direction="row"
      justify="flex-start"
      alignItems="flex-start"
    >
      <DashboardSidebarNavigation />
      <Outlet />
    </Grid>
  );
};

export default Dashboard;

Github repo is here. https://github.com/webmasterdevlin/react-router-6-demo

Solution 8 - Javascript

React Router v6

allows to use both nested routes (like in v3) and separate, splitted routes (v4, v5).

Nested Routes

Keep all routes in one place for small/medium size apps:

<Routes>
  <Route path="/" element={<Home />} >
    <Route path="user" element={<User />} /> 
    <Route path="dash" element={<Dashboard />} /> 
  </Route>
</Routes>

const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        // /js is start path of stack snippet
        <Route path="/js" element={<Home />} >
          <Route path="user" element={<User />} />
          <Route path="dash" element={<Dashboard />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

const Home = () => {
  const location = useLocation()
  return (
    <div>
      <p>URL path: {location.pathname}</p>
      <Outlet />
      <p>
        <Link to="user" style={{paddingRight: "10px"}}>user</Link>
        <Link to="dash">dashboard</Link>
      </p>
    </div>
  )
}

const User = () => <div>User profile</div>
const Dashboard = () => <div>Dashboard</div>

ReactDOM.render(<App />, document.getElementById("root"));

<div id="root"></div>
    <script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/[email protected]/umd/history.production.min.js"></script>
    <script src="https://unpkg.com/[email protected]/umd/react-router.production.min.js"></script>
    <script src="https://unpkg.com/[email protected]/umd/react-router-dom.production.min.js"></script>
    <script>var { BrowserRouter, Routes, Route, Link, Outlet, useNavigate, useLocation } = window.ReactRouterDOM;</script>

Alternative: Define your routes as plain JavaScript objects via useRoutes.

Separate Routes

You can use separates routes to meet requirements of larger apps like code splitting:

// inside App.jsx:
<Routes>
  <Route path="/*" element={<Home />} />
</Routes>

// inside Home.jsx:
<Routes>
  <Route path="user" element={<User />} />
  <Route path="dash" element={<Dashboard />} />
</Routes>

const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        // /js is start path of stack snippet
        <Route path="/js/*" element={<Home />} />
      </Routes>
    </BrowserRouter>
  );
}

const Home = () => {
  const location = useLocation()
  return (
    <div>
      <p>URL path: {location.pathname}</p>
      <Routes>
        <Route path="user" element={<User />} />
        <Route path="dash" element={<Dashboard />} />
      </Routes>
      <p>
        <Link to="user" style={{paddingRight: "5px"}}>user</Link>
        <Link to="dash">dashboard</Link>
      </p>
    </div>
  )
}

const User = () => <div>User profile</div>
const Dashboard = () => <div>Dashboard</div>

ReactDOM.render(<App />, document.getElementById("root"));

<div id="root"></div>
    <script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/[email protected]/umd/history.production.min.js"></script>
    <script src="https://unpkg.com/[email protected]/umd/react-router.production.min.js"></script>
    <script src="https://unpkg.com/[email protected]/umd/react-router-dom.production.min.js"></script>
    <script>var { BrowserRouter, Routes, Route, Link, Outlet, useNavigate, useLocation } = window.ReactRouterDOM;</script>

Solution 9 - Javascript

You can try something like Routes.js

import React, { Component } from 'react'
import { BrowserRouter as Router, Route } from 'react-router-dom';
import FrontPage from './FrontPage';
import Dashboard from './Dashboard';
import AboutPage from './AboutPage';
import Backend from './Backend';
import Homepage from './Homepage';
import UserPage from './UserPage';
class Routes extends Component {
    render() {
        return (
            <div>
                <Route exact path="/" component={FrontPage} />
                <Route exact path="/home" component={Homepage} />
                <Route exact path="/about" component={AboutPage} />
                <Route exact path="/admin" component={Backend} />
                <Route exact path="/admin/home" component={Dashboard} />
                <Route exact path="/users" component={UserPage} />    
            </div>
        )
    }
}

export default Routes

App.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { BrowserRouter as Router, Route } from 'react-router-dom'
import Routes from './Routes';

class App extends Component {
  render() {
    return (
      <div className="App">
      <Router>
        <Routes/>
      </Router>
      </div>
    );
  }
}

export default App;

I think you can achieve the same from here also.

Solution 10 - Javascript

A complete answer for React Router v5.


const Router = () => {
  return (
    <Switch>
      <Route path={"/"} component={LandingPage} exact />
      <Route path={"/games"} component={Games} />
      <Route path={"/game-details/:id"} component={GameDetails} />
      <Route
        path={"/dashboard"}
        render={({ match: { path } }) => (
          <Dashboard>
            <Switch>
              <Route
                exact
                path={path + "/"}
                component={DashboardDefaultContent}
              />
              <Route path={`${path}/inbox`} component={Inbox} />
              <Route
                path={`${path}/settings-and-privacy`}
                component={SettingsAndPrivacy}
              />
              <Redirect exact from={path + "/*"} to={path} />
            </Switch>
          </Dashboard>
        )}
      />
      <Route path="/not-found" component={NotFound} />
      <Redirect exact from={"*"} to={"/not-found"} />
    </Switch>
  );
};

export default Router;
const Dashboard = ({ children }) => {
  return (
    <Grid
      container
      direction="row"
      justify="flex-start"
      alignItems="flex-start"
    >
      <DashboardSidebarNavigation />
      {children}
    </Grid>
  );
};

export default Dashboard;

Github repo is here. https://github.com/webmasterdevlin/react-router-5-demo

Solution 11 - Javascript

I prefer to use react function. This solution is short and more readable

const MainAppRoutes = () => (
    <Switch>
        <Route exact path='/' component={HomePage} />
        {AdminRoute()}                  
        {SampleRoute("/sample_admin")}  
    </Switch>
);

/*first implementation: without params*/
const AdminRoute = () => ([
    <Route path='/admin/home' component={AdminHome} />,
    <Route path='/admin/about' component={AdminAbout} />
]);

/*second implementation: with params*/
const SampleRoute = (main) => ([
    <Route path={`${main}`} component={MainPage} />,
    <Route path={`${main}/:id`} component={MainPage} />
]); 

Solution 12 - Javascript

**This code worked for me with v6**

index.js

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />}>
          <Route path="login" element={<Login />} />
          <Route path="home" element={<Home />} />
        </Route>
      </Routes>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

App.js:

function App(props) {
  useEffect(() => {
    console.log('reloaded');
// Checking, if Parent component re-rendering or not *it should not be, in the sense of performance*, this code doesn't re-render parent component while loading children
  });
  return (
    <div className="App">
      <Link to="login">Login</Link>
      <Link to="home">Home</Link>
      <Outlet /> // This line is important, otherwise we will be shown with empty component
    </div>
  );
}

login.js:

const Login = () => {
    return (
        <div>
            Login Component
        </div>
    )
};

home.js:

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

Solution 13 - Javascript

interface IDefaultLayoutProps {
    children: React.ReactNode
}

const DefaultLayout: React.SFC<IDefaultLayoutProps> = ({children}) => {
    return (
        <div className="DefaultLayout">
            {children}
        </div>
    );
}


const LayoutRoute: React.SFC<IDefaultLayoutRouteProps & RouteProps> = ({component: Component, layout: Layout, ...rest}) => {
const handleRender = (matchProps: RouteComponentProps<{}, StaticContext>) => (
        <Layout>
            <Component {...matchProps} />
        </Layout>
    );

    return (
        <Route {...rest} render={handleRender}/>
    );
}

const ScreenRouter = () => (
    <BrowserRouter>
        <div>
            <Link to="/">Home</Link>
            <Link to="/counter">Counter</Link>
            <Switch>
                <LayoutRoute path="/" exact={true} layout={DefaultLayout} component={HomeScreen} />
                <LayoutRoute path="/counter" layout={DashboardLayout} component={CounterScreen} />
            </Switch>
        </div>
    </BrowserRouter>
);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondatomlView Question on Stackoverflow
Solution 1 - JavascriptLyubomirView Answer on Stackoverflow
Solution 2 - JavascriptdavnicwilView Answer on Stackoverflow
Solution 3 - Javascriptjar0m1rView Answer on Stackoverflow
Solution 4 - JavascriptasukiaaaView Answer on Stackoverflow
Solution 5 - JavascriptmohRamadanView Answer on Stackoverflow
Solution 6 - JavascriptSanjeev ShakyaView Answer on Stackoverflow
Solution 7 - JavascriptDevlin DuldulaoView Answer on Stackoverflow
Solution 8 - Javascriptford04View Answer on Stackoverflow
Solution 9 - JavascriptAniruddh AgarwalView Answer on Stackoverflow
Solution 10 - JavascriptDevlin DuldulaoView Answer on Stackoverflow
Solution 11 - Javascriptseyed mohammad asghariView Answer on Stackoverflow
Solution 12 - Javascriptsampath kumarView Answer on Stackoverflow
Solution 13 - JavascriptEVGENY GLUKHOVView Answer on Stackoverflow