'how to create deep paths in a tree structure in react-router v6

Here is a codesandbox that illustrates the problem:

If I have a tree of objects like this:

const tasks: Task[] = [
  {
    id: 1,
    name: "one",
    children: [
      {
        id: 3,
        name: "three",
        children: [{ id: 5, name: "five", children: [] }]
      }
    ]
  },
  {
    id: 2,
    name: "two",
    children: [
      { id: 4, name: "four", children: [{ id: 6, name: "six", children: [] }] }
    ]
  }
];

I would like to be able to link to a url like this:

/tasks/2/children/4/children/6.

So far I can only get it to link to tasks/2/children/6 with the following route structure:

  let routes: RouteObject[] = [
    {
      path: "tasks",
      element: <Layout />,
      children: [
        {
          path: ":id",
          element: <TaskPage />,
          children: [
            { path: "children/:id", element: <TaskPage />, index: true }
          ]
        },
        {
          index: true,
          element: <TaskTreePage />
        }
      ]
    },
    {
      index: true,
      element: <Navigate to="tasks" />
    }
  ];

  let element = useRoutes(routes);

I would need to continually add a nested route every time I went a new level deep.

Is there a way I could dynamically construct a deeply nested route path that works for any depth?



Solution 1:[1]

When a route is an index route it shouldn't also use a path prop as it's understood the path is that of the wrapping parent layout route.

The TaskPage component is what should be dynamic, not the routes config. The routes config should be the entry point to the "root" route rendering the "root" TaskPage component on "/tasks/:id/*". The TaskPage component is now effectively recursively rendering another instance of itself in a new nested relative route <Route path="children/:id/*" element={<TaskPage />} /> that it renders.

Code example:

App

const routes: RouteObject[] = [
  {
    path: "/tasks",
    element: <Layout />,
    children: [
      {
        path: ":id/*",
        element: <TaskPage />
      },
      {
        index: true,
        element: <TaskTreePage />
      }
    ]
  },
  {
    index: true,
    element: <Navigate to="/tasks" />
  }
];

export default function App() {
  const element = useRoutes(routes);
  return element;
}

TaskPage

This is where the "magic" happens. It renders a new Routes component with two routes, an index route to render the links for the current depth and a route to render the nested children.

function TaskPage() {
  const { id } = useParams();
  const task = findTask(id as string, tasks);

  if (!task) {
    return <h2>Not Found!</h2>;
  }

  return (
    <Routes>
      <Route
        index
        element={
          <div>
            <h2>task of {task.name}</h2>
            <div>
              <Link to="../..">Back</Link>
              <div>
                <h2>Children</h2>
                {task.children.map((child: Task) => (
                  <Link key={child.id} to={`children/${child.id}`}>
                    {child.name}
                  </Link>
                ))}
              </div>
            </div>
          </div>
        }
      />
      <Route path="children/:id/*" element={<TaskPage />} />
    </Routes>
  );
}

Note the trailing wildcard "*" matcher to allow matching of nested routes. Note also the back link is now "../.." since each level of nesting pushes two path segments, so to go "back" two path segments need to be removed.

Edit how-to-create-deep-paths-in-a-tree-structure-in-react-router-v6

const tasks: Task[] = [
  {
    id: 1,
    name: "one",
    children: [
      {
        id: 3,
        name: "three",
        children: [
          {
            id: 5,
            name: "five",
            children: [{ id: 7, name: "seven", children: [] }]
          }
        ]
      }
    ]
  },
  {
    id: 2,
    name: "two",
    children: [
      { id: 4, name: "four", children: [{ id: 6, name: "six", children: [] }] }
    ]
  }
];

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Drew Reese