loading...

How to get React Router 4 to allow nested components inside of a Switch

tylerlwsmith profile image Tyler Smith ・2 min read

Recently, I was building a site's admin section with React. The admin section used a repeated pattern for its URL structure:

  • /admin/:contentType
  • /admin/:contentType/new
  • /admin/:contentType/:id

Because the URL structure was the same for all content types, I had hoped that I could built a component where I passed the content type in as a prop, then have the component build my routes for each content type.

Here was my unsuccessful first-attempt using fragments:

import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

import AdminList from "../admin/list";
import AdminEdit from "../admin/edit";
import AdminNew from "../admin/new";

const AdminRouteGroup = ({ contentType }) => (
  <>
    <Route
      exact
      path={`/admin/${contentType}`}
      render={routeProps => (
        <AdminList contentType={contentType} {...routeProps} />
      )}
    />
    <Route
      exact
      path={`/admin/${contentType}/new`}
      render={routeProps => (
        <AdminNew contentType={contentType} {...routeProps} />
      )}
    />
    <Route
      path={`/admin/${contentType}/:id`}
      render={routeProps => (
        <AdminEdit contentType={contentType} {...routeProps} />
      )}
    />
  </>
);

const App = () => (
  <Router>
    <Switch>
      <AdminRouteGroup contentType="pages" />
      <AdminRouteGroup contentType="posts" />
    </Switch>
  </Router>
);

export default App;

Unfortunately, this doesn't work. On GitHub, I found that React Router collaborator Tim Dorr said the following:

Switch only works with the first level of components directly under it. We can't traverse the entire tree.

Even though the AdminRouteGroup component is rendering a fragment, React Router is still confused because it's expecting a Route component to be its direct child. Instead, it's getting our component AdminRouteGroup.

We can solve this problem with a two fold approach:

  1. We return an array of routes (allowed since React 16) instead of routes contained inside a fragment.
  2. We render the component ourselves instead of returning a JSX component.

When you return an array of components, React expects you to provide a unique key for each component. To make things simple, we'll reuse our path as our key.

Here's what that looks like all together:

import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

import AdminList from "../admin/list";
import AdminEdit from "../admin/edit";
import AdminNew from "../admin/new";

// Have AdminRouteGroup return an array of components.
const AdminRouteGroup = ({ contentType }) => [
  <Route
    exact
    path={`/admin/${contentType}`}
    key={`/admin/${contentType}`}
    render={routeProps => (
      <AdminList contentType={contentType} {...routeProps} />
    )}
  />,
  <Route
    exact
    path={`/admin/${contentType}/new`}
    key={`/admin/${contentType}/new`}
    render={routeProps => (
      <AdminNew contentType={contentType} {...routeProps} />
    )}
  />,
  <Route
    path={`/admin/${contentType}/:id`}
    key={`/admin/${contentType}/:id`}
    render={routeProps => (
      <AdminEdit contentType={contentType} {...routeProps} />
    )}
  />
];

// Render the components directly.
const App = () => (
  <Router>
    <Switch>
      {AdminRouteGroup({ contentType: "pages" })}
      {AdminRouteGroup({ contentType: "posts" })}
    </Switch>
  </Router>
);

export default App;

I hope this helps. Let me know if you found this useful!

Posted on by:

tylerlwsmith profile

Tyler Smith

@tylerlwsmith

I build software. Follow me on Twitter if you want to hear my ramblings! https://twitter.com/tylerlwsmith

Discussion

pic
Editor guide
 

I signed up for dev.to only so that I could like this post and send this comment. I needed to map over an array and return a set of routes which does not work because of the nested Fragment within the Switch. This is a verified workaround. Thank you greatly for taking the time to write this up!

 

Thank you for the kind words! I'm glad you found it useful: this problem had me completely stumped for hours.

 

wow, i had the trouble with nested Fragment in Switch, your answer really really helped me. Thank you so much

 

I'm glad it helped!

 

This made my day, thanks!

 

I'm glad this helped! I got stuck on this problem for hours.