Navigating nested routes in React Router: a comprehensive guide

I need help with integrating nested routes into my app. Can someone guide me on how to add nested routes?

Here is the structure of my app:

import { Route, Routes } from 'react-router-dom';
import routes, { RouteModel } from './router/routes';
import CustomRoute from './router/CustomRoute';

const renderRoutes = (routes: RouteModel[]) => {
  return routes.map((route, index) => {
    if (route.SubItems && route.SubItems.length > 0) {
      return (
        <Route
          key={index}
          path={route.Path}
          element={
            <CustomRoute path={route.Path}>
              <route.Page />
              {renderRoutes(route.SubItems)}
            </CustomRoute>
          }
        />
      );
    } else {
      return (
        <Route
          key={index}
          path={route.Path}
          element={
            <CustomRoute path={route.Path}>
              <route.Page />
            </CustomRoute>
          }
        />
      );
    }
  });
};

const App = () => {
  return <Routes>{renderRoutes(routes)}</Routes>;
};

export default App;

My defined routes are as follows:

import { ComponentType } from 'react';
import CreateCarPage from '../page/car/create/page';
import HomePage from '../page/home/page';
import LoginPage from '../page/login/page';
import ListCarPage from '../page/car/list/page';

export type RouteModel = {
  Path: string;
  Page: ComponentType<any>;
  Text: string;
  SubItems?: RouteModel[];
};

const routes: RouteModel[] = [
  {
    Path: '*',
    Page: HomePage,
    Text: 'Home',
  },
  {
    Path: '/login',
    Page: LoginPage,
    Text: 'Login',
  },
  {
    Path: '/car',
    Page: ListCarPage,
    Text: 'Car',
    SubItems: [
      {
        Path: '/car/create',
        Text: 'Create',
        Page: CreateCarPage,
      },
    ],
  },
];

export default routes;

Below is the implementation of my customRoute:

import { Navigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
import Layout from '../component/layout/Layout';

type Props = {
  children: React.ReactNode;
  path: string;
};

const CustomRoute = ({ children, path }: Props) => {
  const { authenticated } = useAuth();

  if (path === '/login') {
    return authenticated ? <Navigate to="/" /> : children;
  }

  return authenticated ? <Layout>{children}</Layout> : <Navigate to="/login" />;
};

export default CustomRoute;

The error I'm encountering:

Uncaught error: a <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>

Answer №1

renderRoutes generates an array of Route components that require wrapping in the Routes component. The primary level is managed with

<Routes>{renderRoutes(routes)}</Routes>
, but sub-routes within route.SubItems also need to be enclosed in Routes.

When a routed component renders child routes, such as additional routes in a Routes component, the parent path must include a wildcard/splat "*" to the path so that child route paths can be matched and displayed.

const renderRoutes = (routes: RouteModel[]) => {
  return routes.map((route, index) => {
    if (route.SubItems && route.SubItems.length > 0) {
      return (
        <Route
          key={index}
          path={`${route.Path}/*`} {/* <-- add wildcard/splat */}
          element={
            <CustomRoute path={route.Path}>
              <route.Page />
              <Routes>
                {renderRoutes(route.SubItems)} {/* <-- wrapped in Routes! */}
              </Routes>
            </CustomRoute>
          }
        />
      );
    } else {
      return (
        <Route
          key={index}
          path={route.Path}
          element={
            <CustomRoute path={route.Path}>
              <route.Page />
            </CustomRoute>
          }
        />
      );
    }
  });
};

You can enhance the code's conciseness by determining if there are nested routes to display and selectively applying the wildcard/splat to the parent route along with rendering the child routes based on conditions.

const renderRoutes = (routes: RouteModel[]) => {
  return routes.map((route, index) => {
    const hasSubRoutes = !!route.SubItems?.length;

    return (
      <Route
        key={index}
        path={hasSubRoutes ? route.Path : `${route.Path}/*`}
        element={
          <CustomRoute path={route.Path}>
            <route.Page />
            {hasSubRoutes && (
              <Routes>
                {renderRoutes(route.SubItems)}
              </Routes>
            )}
          </CustomRoute>
        }
      />
    );
  });
};

Answer №2

https://i.sstatic.net/lBsIK.png

After pasting the link directly into my browser, it redirects me to the /car page instead of the expected /car/create page.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can I integrate react-router with material-ui button navigation?

I have created a web app using material-UI and implemented Button navigation for navigating through basic landing page components. <div className="footer"> <BottomNavigation value={value} onChange={this.handleChange} className={classes.root}> ...

The 'prop' property is not found in the 'A | B' type

When retrieving data from a URL, if a specific condition is met, the information will be loaded into either type B or its superclass A. However, an issue arises when the data is loaded into B and TypeScript fails to recognize its properties. The code sni ...

Navigating resolvedUrl with getServerSideProps in the newest version of NextJS - a comprehensive guide

Is there a way to obtain the pathname without using client-side rendering? I have been searching for information on how to utilize the getServerSideProps function, but so far with no luck. Initially, I attempted to employ usePathname; however, this result ...

What is the best way to create a memoized function in React?

I am currently developing an application using react and typescript, and I am facing a challenge in memoizing a function. const formatData = ( data: number[], gradientFill?: CanvasGradient ): Chart.ChartData => ({ labels: ["a", ...

Create a personalized button | CKEditor Angular 2

I am currently working on customizing the CKEditor by adding a new button using the ng2-ckeditor plugin. The CKEditor is functioning properly, but I have a specific requirement to implement a button that will insert a Rails template tag when clicked. For ...

Utilize Firebase for Playwright to efficiently implement 'State Reuse' and 'Authentication Reuse'

In my testing environment, I want to eliminate the need for repeated login actions in each test run. My approach involves implementing 'Re-use state' and 'Re-use Authentication', but I've encountered a challenge with Firebase using ...

Using Bootstrap 4 with Angular 2: A Beginner's Guide

Currently, I am in the process of developing an Angular 2 application using TypeScript. My goal is to integrate the Bootstrap 4 framework with some custom theming. Is this achievable? I have encountered issues with the "ng2-bootstrap" npm package, as it d ...

How can I transfer data to a different component in Angular 11 that is not directly related?

Within the home component, there is a line that reads ...<app-root [message]="hii"> which opens the app-root component. The app-root component has an @input and {{message}} in the HTML is functioning properly. However, instead of opening t ...

Sweetalert seems to have hit a roadblock and is not functioning properly. An error has been detected in its TS file

Currently, I am responsible for maintaining an application that utilizes Angular 7.0.7 and Node 10.20.1. Everything was running smoothly until yesterday when my PC unexpectedly restarted. Upon trying to run ng serve, I encountered the following error: E ...

I encountered a problem while integrating antd and moment.js in my React project

I am currently using the antd date-picker in my React project with TypeScript. Encountered an error: Uncaught Type Error: moment is not a function. If anyone has a solution, please assist me. .tsx file:: const dateFormat = 'MM-DD-YYYY'; < ...

Mastering regular expressions in TypeScript

My goal is to perform linting on staged files that are either .ts or .tsx and located within the src folder. I am aware that for selecting all js files one can use "*.js": [--list of commands--] inside the lint staged property. I'm curious to learn m ...

Is React Typescript compatible with Internet Explorer 11?

As I have multiple React applications functioning in Internet Explorer 11 (with polyfills), my intention is to incorporate TypeScript into my upcoming projects. Following the same technologies and concepts from my previous apps, I built my first one using ...

The problem of parameter being NULL in a post request in a .Net Core 3.0 Angular application

This is my first venture into the world of .Net Core Angular projects, so I apologize if my question appears to be too basic. Despite researching similar issues, I am still unable to resolve my problem, which leads me to believe that I must be making a mis ...

Unspecified parameter for Next.js dynamic route

Currently, I am developing an e-commerce application using next.js with Typescript and MongoDB. To better understand my project, let's take a look at my existing file structure: https://i.stack.imgur.com/tZqVm.png The mainPage.tsx file is responsibl ...

What is the process for determining the types of arguments for multiple functions in TypeScript?

const foo = (a: string, b: string) => { console.log('foo', a, b); }; const bar = (a: number, b: number) => { console.log('bar', a, b); }; const multiFactory = <M extends typeof foo | typeof bar>(method: M) => (. ...

Incorporate the Get Your Guide Widget into an Angular2 component

Currently, I am attempting to embed a "Get Your Guide" Widget within an Angular2 application. Although the script in index.html is being requested successfully, I am facing difficulties adding it to the HTML of the component. <script async defer src=" ...

Using React Router and Flux for automated redirection in a programmatic manner

Currently immersed in a project leveraging NodeJS, Express, Flux and React, with the added use of React Router for client-side routing. It appears I might be misunderstanding how the process should ideally function. My present goal is to redirect users to ...

Listening for Internet Connection in Ionic and Angular

Currently, I am working on implementing a listener in my Ionic app that can detect changes in network activity and respond accordingly. import { Component } from '@angular/core'; import { Network } from '@capacitor/network'; @Component ...

The mysterious case of TypeScript imports making all other code vanish

I have multiple classes located in root/app/providers/engine/engine.ts. In my test specification file spec/engine/engine-spec.ts (the spec/ directory is also where the jasmine support/ resides), I have a simple test: ///<reference path="../../typings/g ...

Exploring the retrieval of specific values through bitwise operations in Angular

Currently, I am facing a challenge in retrieving a value that I had initially saved in the database as a sum of bits. My development work is based on Angular 9 using Typescript. I have successfully managed to store the sum of bits in the database. Now, I ...