How to get React Router 4 to allow nested components inside of a Switch
Tags: react, reactrouter, javascript
Published: 2019-10-29 03:07:59

---
title: How to get React Router 4 to allow nested components inside of a Switch
published: true
description: Create a component to group routes in React Router 4.
tags: React, React Router, JavaScript
---

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:

```jsx
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](https://github.com/ReactTraining/react-router/issues/5785#issuecomment-351067856):

> 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:

```jsx
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!