Nested routing is not working in React Router v6

Viewed 18514

I am trying react router v6. As per react training blog, I have created object of routing and passed to useRoutes():

function SomeOtherElement() {
  return <h1>add user</h1>;
}

const routes = [
  {
    path: 'app',
    element: <DashboardLayout />,
    children: [
      { path: 'account', element: <AccountView /> },
      {
        path: 'users', element: <UserListView />, 
        children: [
          { path: 'add', element: <SomeOtherElement /> }
        ]
      },
      { path: 'dashboard', element: <DashboardView /> },
      { path: 'products', element: <ProductListView /> },
      { path: 'settings', element: <SettingsView /> },
      { path: '*', element: <Navigate to="/404" /> }
    ]
  }];

const routing = useRoutes(routes);

But the nested routing is not working. As you can see in above object, I want to create URL and render the UI for user "add" functionality.

URL in the browser is getting updated correctly to http://localhost:3000/app/users/add but UI is not updating.

4 Answers

The default behavior of React Router fails to render multiple child routes with single Outlet. For example - When there is requirement to have a single Header in all the pages and replace the common content (Single Outlet) based on Route.

The trick here is to not provide any element attribute for parent but to provide this component as a child route with the help of Index attribute.

Index helps us to have a default component for a parent route if the child routes doesn't match any of the child routes.

The code snippet for this would be similar to the one provided by Samira.

<BrowserRouter>
  <Routes>
    <Route path="/" element={<App />}>
      <Route path="" element={<Home />}></Route>
      <Route path="child1"> //no element attribute here
        <Route index={true} element={<Child1 />}></Route>
        <Route path="grandChild1" element={<GrandChild1 />}></Route>
        <Route path="grandChild2" element={<GrandChild2 />}></Route>
      </Route>
    </Route>
  </Routes>
</BrowserRouter>

As explained here, you need to use an <Outlet /> element as a placeholder for child i.e. nested routes.

Example:

import { Outlet } from 'react-router-dom'

// ... 

const routes = [
  {
    path: "app",
    element: (
      <>
        <DashboardLayout />
        <Outlet />
      </>
    ),
    children: [
      { path: "account", element: <AccountView /> },
      {
        path: "users",
        element: (
          <>
            <UserListView />
            <Outlet />
          </>
        ),
        children: [{ path: "add", element: <SomeOtherElement /> }],
      },
      { path: "dashboard", element: <DashboardView /> },
    ],
  },
];

Or you may want to have Outlet inside your parent components:

export default function DashboardLayout() {
  return (
    <>
      <h1>I am Dashboard Layout</h1>
      <Outlet />
    </>
  );
}

It is a helpful example for me because I wanted to use nested Route but I didn't want to use Outlet so I found it :

pay attention you can make a dynamic route with path=":teamId"

<BrowserRouter>
        <Routes>
          <Route path="/">
            <Route index={true} element={<Home />} />
          </Route>
          <Route path="/teams">
            <Route index={true} element={<ParentTeam />} />
            <Route index={false} path=":teamId" element={<Team />} />
            <Route index={false} path="new" element={<NewTeamForm />} />
          </Route>
          <Route path="*" element={<Home />} />
        </Routes>
   </BrowserRouter>

link to codeSandBox

For the useRoutes hook the syntax looks like this:

const items = [
  {
    path: "/login",
    element: <Login />,
  },
  {
    path: "/logout",
    element: <Logout />,
  },
  {
    path: "/",
    element:  <RequireAuth element={<Welcome />} />,
  },
  {
    path: "/inbox",
    element:  <RequireAuth element={<Inbox />} />
  },
  {
    path: "/dashboard",
    children: [
      {
        index: true,
        element:  <RequireAuth element={<Dashboard />} />
      },
      {
        path: "setup",
        element:  <RequireAuth element={<DashboardSetup/>} />
      },
    ]
  },


]

export default function MainContent() {
  const router = useRoutes(items);
  return router

}

Related