Search code examples
javascriptreactjsreact-router-dom

How do i use nested routes without having components within parent route components?


Does using react-router-dom's nested routes force you to render nested route components within parent route components? I'm new to routes and I am trying to understand.

I have a React app simulated below, and I brought in react-router-dom v6 a bit late in the development process. ComponentView renders the needed component based on title prop. I have the routes set up like below where "/a" & "/b" are mapped to a ComponentView component rendered according to the title value. What I want is to make "/b" a nested route of "/a" without changing the component structure. Is that possible?

I tried restructuring the routes like this:

<Route path="a" element={<ComponentView title="a" />}>
  <Route path="b" element={<ComponentView title="b" />} />
</Route>

...but if I then navigate to "/a/b", I get the equivalent of "/a".

From what I know of react router so far, if I want to make "/b" a sub of "/a", that means the component rendered in "/b" would be rendered in addition to whatever component(s) is rendered by "/a". Is there a way to nest the routes, but not nest the content?

App.js

import React from "react";
import { Route, Routes } from 'react-router-dom';
import './App.css';
import { Home } from "./components/Home"
import { ComponentView } from "./components/ComponentView";

function App() {
  return (
    <div className="App">
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="a" element={<ComponentView title="a" />} />
        <Route path="b" element={<ComponentView title="b" />} />
      </Routes>
    </div>
  );
}
export default App;

ComponentView.js

import { ComponentA } from "./ComponentA";
import { ComponentB } from "./ComponentB";

export function ComponentView(props) {
  return (
    <div>
      {props.title === "a" ? (
        <ComponentA title={props.title} />
        ) : (
        <ComponentB title={props.title} />
      )}
    </div>
  );
}

ComponentA.js

export function ComponentA(props) {
  return(
    <div>
      <div style={{ height: "5rem", width: "100%" }}>
        Component A - title: {props.title}
      </div>
    </div>
  );
}

ComponentB.js

export function ComponentB(props) {
  return(
    <div>
      <div style={{ height: "5rem", width: "100%" }}>
        Component B - title: {props.title}
      </div>
    </div>
  );
}

Solution

  • ComponentView doesn't render an Outlet for nested routes to render out their element content, so

    <Route path="a" element={<ComponentView title="a" />} >
      <Route path="b" element={<ComponentView title="b" />} />
    </Route>
    

    renders only <ComponentView title="a" />, the nested route isn't reachable/renderable.

    If I am understanding correctly that you want ComponentA to be rendered only when the path is "/a", and for ComponentB to be rendered on path "/a/b", then refactor the routes to render <ComponentView title="a" /> as the index route of "/a" and <ComponentView title="b"/> as the nested "b" route of "/a". This way each will be rendered independent of the other.

    Example:

    function App() {
      return (
        <div className="App">
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="a">
              <Route index element={<ComponentView title="a" />} />
              <Route path="b" element={<ComponentView title="b" />} />
            </Route>
          </Routes>
        </div>
      );
    }