reactjsreact-router-domnested-routes

How to create a correct URL according to nested routes?


I am trying to create some nested routes in a react project using the useRoutes hook. But I am struggling to make the URL show as I want.

The example struture project is this one:

- public
- src
  - Components
    - Header.js
    - MaterialsDetails.js
    - Materials.js
    - TitleDetails.js
    - Titles.js
- App.js
- index.js
- routes.js

I have create the routes in routes.js like this:

import { Outlet } from "react-router-dom";
import Titles from "./Components/Titles";
import TitleDetails from "./Components/TitleDetails";
import Materials from "./Components/Materials";
import MaterialDetails from "./Components/MaterialDetails";
import Header from "./Components/Header";

const routes = [
  {
    path: "/",
    element: <Header />,
    children: [
      {
        path: "/titles",
        element: <Outlet />,
        children: [
          {
            index: true,
            element: <Titles />
          },
          { path: ":id", element: <TitleDetails /> }
        ]
      },
      {
        path: "/materials",
        element: <Outlet />,
        children: [
          {
            index: true,
            element: <Materials />
          },
          { path: ":id", element: <MaterialDetails /> }
        ]
      }
    ]
  }
];
export default routes;

Then I have the HeaderLayout.js that will be shown in every page:

import React from "react";
import { Outlet } from "react-router-dom";

export default function HeaderLayout() {
  return (
    <>
      <h1>Header Layout</h1>
      <Outlet />
    </>
  );
}

In titles.js I have a list of titles where each title has its details when clicking on it:

import React from "react";
import { Link } from "react-router-dom";

export default function Titles() {
  return (
    <>
      <h3>Titles</h3>
      <p>
        <Link to="1">Title example 1</Link>
      </p>
      <p>
        <Link to="2">Title example 2</Link>
      </p>
      <p>
        <Link to="3">Title example 3</Link>
      </p>
    </>
  );
}

Then in TitleDetails.js apart of having the title detail I have a link to go to the related materials. So when I click I will go to the list of materials:

import React from "react";
import { useParams } from "react-router-dom";
import { Link } from "react-router-dom";

export default function TitleDetails() {
  const { id } = useParams();
  return (
    <>
      <h1>Details of title {id}</h1>
      Related materials: <Link to="/materials">Materials</Link>
    </>
  );
}

And finally in Materials.js I have the same concept as Titles.js. A list of materials that when I click on one I will go to MaterialDetails.js

import React from "react";
import { Link } from "react-router-dom";
export default function Materials() {
  return (
    <>
      <h3>Materials</h3>
      <p>
        <Link to="1">Material example 1</Link>
      </p>
      <p>
        <Link to="2">Material example 2</Link>
      </p>
      <p>
        <Link to="3">Material example 3</Link>
      </p>
    </>
  );
}

The question is how can I modify routes.js to obtain, for example, that URL: http://localhost:3000/titles/1/materials/3 instead of going to http://localhost:3000/materials when I amb in http://localhost:3000/titles/1?

Thank you!


Solution

  • I found the solution to my problem:

    I changed routes.js to:

    import { Outlet } from "react-router-dom";
    import Titles from "./Components/Titles";
    import TitleDetails from "./Components/TitleDetails";
    import Materials from "./Components/Materials";
    import MaterialDetails from "./Components/MaterialDetails";
    import HeaderLayout from "./Components/HeaderLayout";
    
    const routes = [
      {
        path: "/",
        element: <HeaderLayout />,
        children: [
          {
            path: "/titles",
            element: <Outlet />,
            children: [
              {
                index: true,
                element: <Titles />
              },
              { path: ":id", element: <TitleDetails /> },
              { path: ":id/materials", element: <Materials /> },
              { path: ":id/materials/:idMaterial", element: <MaterialDetails /> }
            ]
          }
        ]
      }
    ];
    export default routes;
    

    and then in TitleDetails.js I removed the / of /materials:

    import React from "react";
    import { useParams } from "react-router-dom";
    import { Link } from "react-router-dom";
    
    export default function TitleDetails() {
      const { id } = useParams();
      return (
        <>
          <h1>Details of title {id}</h1>
          Related materials: <Link to="materials">Materials</Link>
        </>
      );
    }