React Router v4 with multiple layouts

Solution 1:

What I have done for this is create a simple component that adds an extra property to the Route component which is layout:

function RouteWithLayout({layout, component, ...rest}){
  return (
    <Route {...rest} render={(props) =>
      React.createElement( layout, props, React.createElement(component, props))
    }/>
  );
}

Then in your case your routes would look like this

<Switch>
    <RouteWithLayout layout={PublicLayout} path="/" component={HomePage}/>
    <RouteWithLayout layout={PublicLayout} path="/about" component={AboutPage}/>
    <RouteWithLayout layout={PrivateLayout} path="/profile" component={ProfilePage}/>
    <RouteWithLayout layout={PrivateLayout} path="/dashboard" component={DashboardPage}/>
</Switch>

Solution 2:

UPDATE 2020

Well for now I'm following this approach, it's simpler that the one I posted before:

const Pages = () => {
  return (
    <ReactRouter>
      <Switch>
        <Route path="/comingsoon" component={ComingSoon} exact />
        <Route>
          <MainLayout>
            <Switch>
              <Route path="/home" exact>
                <Home />
              </Route>
              <Route path="/login" exact>
                <Login />
              </Route>
              <Route path="/useraccount" exact>
                <UserAccount />
              </Route>
              <Route path="/createaccount" exact>
                <CreateAccount />
              </Route>
              <Route path="/contact" exact>
                <Contact />
              </Route>
              <Route path="/about" exact>
                <About />
              </Route>
              <Redirect path="/" exact to="/comingsoon" />
              <Route path="*" exact component={NotFound} />
            </Switch>
          </MainLayout>
        </Route>
      </Switch>
    </ReactRouter>
  );
};

In this way, the MainLayout will take care of everything except for the coming soon page.

OLD ANSWER

If you are using Typescript and want to follow this react layout aproach then you can declare your layout like this:

import './Default.less';

import React from 'react';
import { Route } from "react-router-dom";

import { Sider } from './Components';
import { Notification } from 'Client/Components';

interface IDefaultProps {
  component: any
  path?: string;
  exact?: boolean;
}

const Default: React.SFC<IDefaultProps> = (props) => {
  const { component: Component, ...rest } = props;
  return <Route {...rest} render={matchProps => (
    <div className="defaultLayout">
      <Sider />
      <div className="defaultLayoutContent">
        <Component {...matchProps} />
      </div>
      <Notification />
    </div>
  )} />
}

export default Default;

And declare routes like this:

import React from 'react';
import { Route } from 'react-router-dom';

import { DefaultLayout } from 'Client/Layout';
import { Dashboard, Matters, Schedules, Students } from './Containers';

export const routes = <div>
  <DefaultLayout exact path="/" component={Dashboard} />
  <DefaultLayout path="/matters" component={Matters} />
  <DefaultLayout path="/schedules" component={Schedules} />
  <DefaultLayout path="/students" component={Students} />
</div>;