Published on

Dynamic routes in react-router-dom

Authors

where are we? in a simple react app, we want to implement dynamic routes using objects to have a scalable router for the whole app.

we assume we want to have these routes:

  • /dashboard/account/view
  • /dashboard/posts
  • /dashboard/posts/:id

we can manage our route mapping by creating nested objects and arrays. we can generate types for this structure like this:

export type TRoute = {
  path: string
  Page: React.FunctionComponent
  subRoutes?: TRoute[]
}

export type TRouteCollection = {
  [key in string]: TRouteCollection | Array<TRoute>
}

by Page I mean any functional component like this one:

import React from 'react'

export const SamplePage: React.FunctionComponent = () => {
  return <div>sample page</div>
}

now we can create our route object. this object can be a nested object or be separated based on the needs of the project. but the structure should follow the types we declared above. for example, this is the mapping that we need for our app routes:

const routeList = {
    dashboard: {
        account: [
            path: "view",
            Page: AccountPage,
        ],
        posts: [
            path: "",
            Page: PostsListPage,
            subRoutes: [
                {
                    path: ':id',
                    Page: SinglePostPage,
                },
            ],
        ],
    }
}

now we need a function to loop over our route mapping object and generate our routes. this function uses recursion and calls itself based on the value it loops over.

const buildRouteNavigation = (routes: Array<TRoute> | TRouteCollection): React.ReactNode => {
  if (Array.isArray(routes)) {
    return routes.map(({ Page, path, subRoutes }) => {
      return (
        <React.Fragment key={path}>
          <Route path={path}>
            <Route path="" element={<Page />} />
            {subRoutes?.length && buildRouteNavigation(subRoutes)}
          </Route>
        </React.Fragment>
      )
    })
  }

  return Object.entries(routes).map(([path, pages]) => {
    return (
      <React.Fragment key={path}>
        <Route path={path}>{buildRouteNavigation(pages)}</Route>
      </React.Fragment>
    )
  })
}

in the end, all we have to do is to put this function in right place (e.g. as children of BrowserRouter), and pass our route mapping object (in our example, routeList) to it.

import { BrowserRouter, Route, Routes } from 'react-router-dom'

const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<DashboardPage />} />
        {buildRouteNavigation(routeList)}

        <Route path="*" element={<NotFoundPage />} />
      </Routes>
    </BrowserRouter>
  )
}

export default App

this is a simple way of implementing dynamic routes in react-router-dom. this method will help us to make more modular code and also helps code splitting.

by the way, this is not the end of the process. there are a lot of things that we can implement using this method, like a custom layout for each route.

hope you enjoyed it!