Search code examples
javascriptreactjsreact-routerreact-router-dom

In React Router 6, what is the common practice for nesting routes in separate components?


A common pattern in the vast majority of routing system is to allow the refactoring out of groups of routes to reduce complexity and the size of files (and sometimes also to make it easy to apply changes across a group of routes). Otherwise you could end up with a super long file with a lot of dependencies.

When I look in the react router docs to see what they say about this I see:

(TODO: need to talk about nesting, maybe even a separate doc)

So say I have a DefaultRoutes component that uses a <Routes> component and some <Route> children with a set of some patient routes:

return (
  <Routes>
    {/*..other routes*/}
    <Route path="/patients" element={
      <RequireAuth>
        <DefaultLayout />
      </RequireAuth>
    }>
      <Route index element={<Patients />} />
      <Route path=":patientId">
        <Route index element={<PatientDetails />} />
        <Route path="edit" element={<EditPatient />} />
      </Route>

      <Route path="new">
        <Route index element={<NewPatient />} />
        <Route path=":clinicId" element={<NewPatient />} />
      </Route>
    </Route>
    {/*..other routes*/}
  </Routes>
);

How would I refactor my patient routes into a separate component (PatientRoutes) or whatever, just so they weren't in the same file at the least?


Solution

  • When you want to "code split" and define the currently nested routes into separate components to then be rendered as descendent routes the change is simple.

    1. Extract the nested routes group you would like to render separately into its own component. Descendent routes will need to be rendered into another Routes component, and the paths build relative to the parent route similar to nested routes.

      const PatientRoutes = () => (
        <Routes>
          <Route element={
            <RequireAuth>
              <DefaultLayout />
            </RequireAuth>
          }>
            <Route path="/" element={<Patients />} />
            <Route path=":patientId">
              <Route index element={<PatientDetails />} />
              <Route path="edit" element={<EditPatient />} />
            </Route>
      
            <Route path="new">
              <Route index element={<NewPatient />} />
              <Route path=":clinicId" element={<NewPatient />} />
            </Route>
          </Route>
        </Routes>
      );
      
    2. Update the parent route to render the new descendent routes component with a path that allows descendent route path matching by appending a wildcard "*", or Splat, matcher to the path.

      return (
        <Routes>
          {/* ..other routes */}
      
          <Route path="/patients/*" element={<PatientRoutes />} />
      
          {/* ..other routes */}
        </Routes>
      );