skip to Main Content

I have a page layout with 3 separate sections: The sidebar, the header and the main body. I would like to fill the main body from the child route, while parent routes define the header or sidebar – but after trying to work with Providers and Context approaches, I couldn’t quite find the right approach.

Here’s a baseline / pseudo-code of what I would like to achieve:

Example Layout:

layout.tsx

export function DefaultLayout() {
  return (
    <div class="sidebar">
      <!-- sidebar content goes here -->
    </div>
    <div class="page">
      <div class="page-header">
        <!-- header content goes here -->
      </div>
      <div class="page-body">
        <Outlet />
      </div>
    </div>
  );
}

Example routes:

router

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

<Route element={<DefaultLayout />}>
  <Route
    path="/dashboard"
    sidebar={<DefaultSidebar />}
    header={<DashboardHeader />}
  >
    <Route path="home" element={<DashboardHomePage />}>
    <Route path="stats" element={<DashboardStatsPage />}>
    <Route path="help" element={<DashboardHelpPage />}>
  </Route>
  <Route path="/user" sidebar={<UserSidebar />} header={<UserHeader />}>
    <Route path="overview" element={<UserOverviewPage />}>
    <Route path="accounts" element={<UserAccountsPage />}>
  </Route>
</Route>

As seen in the simplified example above, I would like to define the overall Layout in a parent route, while "filling" the layout sections in nested routes. sidebar={} and header={} are just pseudo-code for the example, I realize that it’s probably part of another element={} wrapper.

I’ve tried solving this by creating a <SidebarProvider> and <HeaderProvider> component, working with things like useOutletContext and passing the state downwards, but that didn’t seem to work and was quickly getting hopelessly complicated (infinite loops, etc).

So, starting fresh: what would be a method to achieve the above behavior? If it’s via providers, how should I structure them?

2

Answers


  1. If I understood correctly you want to render dynamically only one part of the app. In that case you can directly embed the routes inside your "dynamic" section.
    In you case your Layout component would become:

    export function DefaultLayout() {
        return (
            <BrowserRouter>
            <div class="sidebar">
                <!-- sidebar content goes here -->
            </div>
            <div class="page">
                <div class="page-header">
                    <!-- header content goes here -->
                </div>
                <div class="page-body">
                    <Routes>
                        //..your routes here
                    </Routes>
                </div>
            </div>
            </BrowserRouter>
        );
    }
    

    I made also a demo of what It’d look like https://codesandbox.io/s/naughty-lalande-j3378g?file=/src/App.js:318-322.

    Login or Signup to reply.
  2. I would suggest adding the header and sidebar as props to the DefaultLayout route component and render them accordingly. Don’t forget that in React we use a className prop instead of class.

    const DefaultLayout = ({ header, sidebar }) => (
      <div className="layout">
        <div className="sidebar">{sidebar}</div>
        <div className="page">
          <div className="page-header">{header}</div>
          <div className="page-body">
            <Outlet />
          </div>
        </div>
      </div>
    );
    

    Note that I added a "layout" class to the overall surrounding element of the entire element in order to set the overall page layout using a grid layout and assigned areas.

    Example CSS:

    .layout {
      display: grid;
      grid-template-areas: "sidebar page";
    }
    
    .sidebar {
      grid-area: sidebar;
    }
    
    .page {
      grid-area: page;
    }
    

    You will then adjust the routes to pass the sidebar and header components to the layout route each.

    <Routes>
      <Route
        element={
          <DefaultLayout
            sidebar={<DefaultSidebar />}
            header={<DashboardHeader />}
          />
        }
      >
        <Route path="/dashboard">
          <Route path="home" element={<DashboardHomePage />} />
          <Route path="stats" element={<DashboardStatsPage />} />
          <Route path="help" element={<DashboardHelpPage />} />
        </Route>
      </Route>
      <Route
        element={
          <DefaultLayout sidebar={<UserSidebar />} header={<UserHeader />} />
        }
      >
        <Route path="/user">
          <Route path="overview" element={<UserOverviewPage />} />
          <Route path="accounts" element={<UserAccountsPage />} />
        </Route>
      </Route>
    </Routes>
    

    Edit how-to-pass-elements-to-other-page-layout-sections-from-inside-a-nested-route

    enter image description here
    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search