20

Is there a way to nest routes in React Router v4?

This works:

  <Router basename='/app'>
    <main>
      <Route path='/' component={AppBar} />
      <Route path='/customers' component={Customers} />
    </main>
  </Router>

This does not:

  <Router basename='/app'>
    <Route path='/' component={AppBar}>
      <Route path='/customers' component={Customers} />
    </Route>
  </Router>

Customers Component:

import React, { Component, PropTypes } from 'react'
import styled from 'styled-components'

export default class Customers extends Component {
  render () {
    return (
      <Container>
        <h1>Customers</h1>
      </Container>
    )
  }
}

const Container = styled.section`
  height: 100%;
  padding: 15px;
  overflow: auto;
`
Raphael Rafatpanah
  • 15,663
  • 19
  • 73
  • 136

6 Answers6

37

Best pattern I have found so far.

// main app
<div>
    // not setting a path prop, makes this always render
    <Route component={AppShell}/>
    <Switch>
        <Route exact path="/" component={Login}/>
        <Route path="/dashboard" component={AsyncDashboard(userAgent)}/>
        <Route component={NoMatch}/>
    </Switch>
</div>

I can just keep nesting this inside a component and everything works nice including hmr(If using webpack, dont forget to set output.publicPath to "/")

// dashboard component
<div>
    // the same way as before, not setting a path prop
    // makes it render on every /dashboard/** request 
    <Route component={DashboardTAB}/>
    <Switch>
        // longer path (with same root) than others first
        <Route path="/dashboard/graphs/longerpath" component={GraphForm}/>
        <Route path="/dashboard/graphs" component={Graphs}/>
        <Route path="/dashboard/workers" component={List}/>
        <Route path="/dashboard/insert" component={InsertComponent}/>
    </Switch>
</div>
pirs
  • 1,992
  • 2
  • 14
  • 23
CESCO
  • 5,396
  • 4
  • 41
  • 73
  • I did like that but with a master parent layout, that's running correctly without error but showing the first route in only (in the component), and no page change when i click a link, im using webpack too and put output.publicPath as you said.. any idea for another issue ? thx – pirs Apr 24 '17 at 16:48
  • 1
    Perfect that's alright, i just started the first route with the shortest path so that took the shortest path as the only one available. – pirs Apr 24 '17 at 21:09
  • 1
    Not setting path prop solved the problem I was having. Thanks! – NaN Aug 31 '17 at 19:37
8

I adapted this from the docs, seem to work so far. Probably missing something obvious, and yes it is not the v4 way but we need all the routes defined in one place.

function RouteNest(props){ return (
  <Route exact={props.exact} path={props.path} render={ p => <props.component {...p} children={props.children}/> } />
)}

export const MainRoutes = props => 

<div className='content layout'>

  <Route exact path="/" component={Landing}/>
  <Route  path={'/contact'} component={Contact}/>

  <RouteNest  path={'/thing'} component={CompoWithSub}>
    <RouteNest  path={'/thing/suba'} component={SubComponentA}/>
    <RouteNest  path={'/thing/subb'} component={SubComponentB}/>
  </RouteNest>


</div>

export const CompoWithSub = props => <div>{props.children)</div>
soundyogi
  • 349
  • 3
  • 14
1

You're AppBar component is in charge of rendering Customers. For customers to be called, you have to render the children of AppBar. Anything directly nested under AppBar is a child of AppBar.

import React from 'react';

const AppBar = ({ children }) => (
  <div>
    <header>
       <h1> stuff </h1>
    </header>
    {children}
  </div>
);

export default AppBar

Please note that only AppBar will render when you visit "/". AppBar and Customers will render when you visit "/customers".

ExperimentsWithCode
  • 1,124
  • 4
  • 13
  • 30
1

If someone wants to have nested routes without typing prefix of wrapper route I've created something like this in TSX:

Imports:

import * as React from 'react';
import { Route, RouteComponentProps, RouteProps, Switch } from 'react-router-dom';
import Index from 'views/index';
import Login from 'views/login';
import NoMatch from 'views/no-match';

Interfaces:

interface INestedRoutes {
    nested?: string;
}

interface INestedRoute extends RouteProps, INestedRoutes {}

NestedRoute and NestedRoutes wrapper:

class NestedRoutes extends React.Component<INestedRoutes> {
    public render() {
        const childrenWithProps = React.Children.map(this.props.children, (child) => {
            return React.cloneElement(
                child as React.ReactElement<any>, { nested: this.props.nested },
            );
        })
        return childrenWithProps;
    }
}


const NestedRoute: React.SFC<INestedRoute> = (props: INestedRoute) => {
    return <Route path={`${props.nested}${props.path}`} component={props.component} />;
};

And routes with wrapper:

const MultiLanguage: React.SFC<RouteComponentProps<any>> = (props: RouteComponentProps<any>) => {
    return (
        <NestedRoutes nested={props.match.path} >
            <NestedRoute path="/test" component={Login} />
            <NestedRoute path="/no-match" component={NoMatch} />
        </NestedRoutes>
    );
};


export default (
    <Switch>
        <Route path="/:language" component={MultiLanguage}/>
        <Route exact={true} path="/" component={Index} />
        <Route path="/login" component={Login} />
        <Route component={NoMatch} />
    </Switch>
);
0

For nested routes there is a very simple way which i using.

Example main router is be like that

<Router history={history}>
  <Switch >
    <Route path="/" component={Home}></Route>
  </Switch>
</Router>

Inside Home component using Nested Routing be like:

<div className="App">
  <Navbar title="Home" links = { NavbarLinks }/>
  {this.renderContentPage()}
</div>

renderContentPage will check the URL and render the nested route.

<Route exact path="/" component={Page1}></Route>
<Route exact path="/page1" component={Page1}></Route>
<Route exact path='/page2' component={Page2} />

So inside Home component page1 and page2 components rendered.

Hadnazzar
  • 926
  • 12
  • 15
0

Route expects a single children i.e. a component. It should not be a new Route. What you can do is to include your nested routes inside your customers component.

Also make sure to remove exact inside the routes in customers component.

Deevoid
  • 61
  • 6