33

If I have the following:

<Route path="/" component={Containers.App}>

  { /* Routes that use layout 1 */ }
  <IndexRoute component={Containers.Home}/>
  <Route path="about" component={Containers.About}/>
  <Route path="faq" component={Containers.Faq}/>
  <Route path="etc" component={Containers.Etc}/>

  { /* Routes that use layout 2 */ }
  <Route path="products" component={Containers.Products}/>
  <Route path="gallery" component={Containers.Gallery}/>
</Route>

How can I make it so that the two sets of routes each use a different layout.

If I only had a single layout then I would put it in App, but in this case where do I define the layout?

To make it even more complicated some of the layout components (eg top nav) are shared between both layout types.

1
  • 1
    See this answer for React Router v4 Commented Jun 21, 2017 at 3:30

7 Answers 7

41

You can use routes without a path to define containers that are not defined by the url:

<Route path="/" component={Containers.App}>

  { /* Routes that use layout 1 */ }
  <Route component={Containers.Layout1}>
    <IndexRoute component={Containers.Home}/>
    <Route path="about" component={Containers.About}/>
    <Route path="faq" component={Containers.Faq}/>
    <Route path="etc" component={Containers.Etc}/>
  </Route>

  <Route component={Containers.Layout2}>
    { /* Routes that use layout 2 */ }
    <Route path="products" component={Containers.Products}/>
    <Route path="gallery" component={Containers.Gallery}/>
  </Route>
</Route>

The layout components can then import additional components such as the top nav

Sign up to request clarification or add additional context in comments.

7 Comments

Can you make a gist with your Layout1 and Layout 2 ? It will be helpful. Thks
You just need to be calling {this.props.children} somewhere in your render function. This is a presentational component
@SystemicPlural the mentioned github repo not exist anymore , any alternative repo available ?
Does this method no longer work? All given links are dead and I cannot get this to function as intended in my project.
Soo... here's what I found out (using Meteor Base by The Meteor Chef FYI): I moved the route that I wanted to use a different layout than my normal one to be above my other routes in routes.js. After that, everything worked perfectly. For some reason the route I was trying to get to was giving me a 404 otherwise.
|
26

Route's path property has accepted an array of strings for a while now. See https://github.com/ReactTraining/react-router/pull/5889/commits/4b79b968389a5bda6141ac83c7118fba9c25ff05

Simplified to match the question routes, but I have working multiple layouts essentially like this (using react-router 5):

<App>
  <Switch>
    <Route path={["/products", "/gallery"]}>
      <LayoutTwo>
        <Switch>
          <Route path="/products" component={Products} />
          <Route path="/gallery" component={Gallery} />
        </Switch>
      </LayoutTwo>
    </Route>
    {/* Layout 1 is last because it is used for the root "/" and will be greedy */}
    <Route path={["/about", "/faq", "/etc", "/"]}>
      <LayoutOne>
        <Switch>
          <IndexRoute component={Home} />
          <Route path="/about" component={About} />
          <Route path="/faq" component={Faq} />
          <Route path="/etc" component={Etc} />
        </Switch>
      </LayoutOne>
    </Route>
  </Switch>
</App>

This solution prevents re-mounting the layouts on route changes, which can break transitions, etc.

1 Comment

This worked for me with react router v5.
9

Here's a great way to use multiple layouts with different React components.

In your router you can use:

<Router history={browserHistory}>
  <Route component={MainLayout}>
    <Route path="/" component={Home} />
    <Route path="/about" component={About} />
  </Route>
  <Route component={EmptyLayout}>
    <Route path="/sign-in" component={SignIn} />
  </Route>
  <Route path="*" component={NotFound}/>
</Router>

enter image description here

Source: https://sergiotapia.me/different-layouts-with-react-router-71c553dbe01d

3 Comments

This is basically same as decorators
What if the MainLayout has header & footer and you want to stick your Route just between them?
@vsync Then you make sure to render {this.props.children} between the header and the footer in the MainLayout component.
8

This is how it's done in React Router v6:

    <Route path="/" element={<App />}>
      {/* Routes that use layout 1 */}
      <Route element={<BlueLayout />}>
        <Route index element={<Home />} />
        <Route path="about" element={<About />} />
        <Route path="faq" element={<Faq />} />
        <Route path="etc" element={<Etc />} />
      </Route>

      {/* Routes that use layout 2 */}
      <Route element={<RedLayout />}>
        <Route path="products" element={<Products />} />
        <Route path="gallery" element={<Gallery />} />
      </Route>
    </Route>

I've created a complete example in StackBlitz here.

1 Comment

This is the correct solution in 2023
7

Pintouch, I was able to get this working with the following example:

Layout1:

import React from 'react'

const Layout1 = (props) => (
    <div>
        <h1>Layout 1</h1>
        {props.children}
    </div>
)

export default Layout1

Layout2:

import React from 'react'

const Layout2 = (props) => (
    <div>
        <h1>Layout 2</h1>
        {props.children}
    </div>
)

export default Layout2

Layout Container:

import React from 'react'

const LayoutContainer = (props) => (
    <div>
                {props.children}
    </div>
)

export default LayoutContainer

Routes:

import React from 'react';
import { Router, Route, IndexRoute, hashHistory } from 'react-router';

import LayoutContainer from './LayoutContainer'
import Layout1 from './Layout1'
import Layout2 from './Layout2'
import ContactManagerView from './ContactManagerView'
import CallerIdView from './CallerIdView'
import NotFound from './NotFound'

<Router history={hashHistory}>
    <Route path="/" component={LayoutContainer}>
        <Route component={Layout1}>
            <IndexRoute component={DashboardView}/>
            <Route path='Contacts' component={ContactManagerView}/>
        </Route>

        <Route component={Layout2}>
            <Route path='CallerId' component={CallerIdView}/>
        </Route>

        <Route component={Layout}>
            <Route path='*' component={NotFound}/>
        </Route>
    </Route>
</Router>

2 Comments

A+ for completeness.
Seems like React does not like this: Warning: You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored - solution in this post
2

You could create a function RouteWithLayout that renders the <Route> wrapped within the layout:

const RouteWithLayout = ({ component: Component, layout: Layout, ...rest }) => (
  <Route {...rest} render={props => (
    <Layout>
      <Component {...props} />
    </Layout>
  )} />
)

const MainLayout = props => (
  <div>
    <h1>Main</h1>
    {props.children}
  </div>
)

const AltLayout = props => (
  <div>
    <h1>Alt</h1>
    {props.children}
  </div>
)

const Foo = () => (
  <p>Foo</p>
)

const Bar = () => (
  <p>Bar</p>
)

const App = () => (
  <div>
    <Switch>
      <RouteWithLayout exact path="/foo" layout={MainLayout} component={Foo} />
      <RouteWithLayout exact path="/bar" layout={AltLayout} component={Bar} />
    </Switch>
  </div>
)

1 Comment

For my use case this was the cleanest approach. Thanks!
1

I came across this question and found a solution that I want to share.

With react router v4 we could render the routes directly in your layout. Which is more readable and easy to maintain.

Layout

export class MainLayout extends React.PureComponent {
  render() {
    return (
      <div>
        <Header />
        {this.props.children}
        <Footer />
      </div>
    );
  }
}

Mainlayout.propTypes = {
  children: PropTypes.node.isRequired,
}

Router

export default function App() {
  return (
    <Switch>
      <MainLayout>
        <Switch>
          <Route path="/" component={Home} />
          <Route path="/about" component={About} />
        </Switch>
      </MainLayout>
      <OtherLayout>
        .... other paths
      </OtherLayout>
    </Switch>
  );
}

1 Comment

Using 2 switches worked! but when to use two switches?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.