React - dynamic routing
Tags: react, beginners, programming, webdev
Published: 2022-06-22 23:46:51


## **What are we going to learn?**

   1. How to implement dynamic routing.
   2. How to mirror route changes to some kind of navigation.
   3. How to implement "Not Found" page.

![End result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ik4ry8msn1sxf15s34c2.png)
 
 

## Notes
1. This example will use [React Suite](https://rsuitejs.com/) pack of UI components.

2. Code can be found [here](https://github.com/ikranjec99/dynamic-routing).

## 1. Implementing dynamic routing and mirroring changes to sidebar navigation

Here we need to create a **list of routes**. 

```
export const Router = [
    {
        title: "Home",
        icon: <FolderFillIcon />,
        route: "",
        component: <Home />
    },
    {
        title: "About us",
        icon: <GridIcon />,
        children: [
            {
                title: "Contact us",
                icon: <ReviewIcon />,
                route: "/contact-us",
                component: <Contact />
            },
            {
                title: "Become a partner",
                icon: <GlobalIcon />,
                route: "/partner-program",
                component: <PartnerProgram />
            }
        ]
    }
]
```

- Title - name of the route in navigation.
- Icon  - suitable icon component from React Suite
- Route - path to page
- Component - this will represent a page we wish to render at a current route

It is time to create a navigation component. 
**Navigation** will be implemented via [Sidenav](https://rsuitejs.com/components/sidenav/#sidenav) component from rsuite.

In case there are children on current route, we should render some kind of navigational menu (dropdown), and use children to display them as navigation items (links).

If children are not present in current route, then just render a simple navigation item (link).

This part of navigational logic is implemented via **NavigationItem** component.

```
export const NavigationItem = ({ icon, route, title, childRoutes }) => {
  const navigate = useNavigate();

  return (
    <>
      {childRoutes && (
        <Nav.Menu title={title} icon={icon}>
          {childRoutes &&
            childRoutes.map((x) => {
              return (
                <Nav.Item
                  onClick={() => navigate(x.route)}
                  children={x.title}
                  icon={x.icon}
                />
              );
            })}
        </Nav.Menu>
      )}
      {!childRoutes && (
        <Nav.Item
          onClick={() => navigate(route)}
          children={title}
          icon={icon}
        />
      )}
    </>
  );
};
```

**Navigation** component implementation should look like this. 
Routes are rendered via **NavigationItem** component inside **Nav** component.

```
export const Navigation = ({
  appearance,
  expanded,
  onOpenChange,
  onExpand,
  ...navProps
}) => {
  return (
    <div className={"navigation"}>
      <Sidenav
        appearance={appearance}
        expanded={expanded}
        onOpenChange={onOpenChange}
      >
        <Sidenav.Body>
          <Nav {...navProps}>
            {Router.map((x) => {
              return (
                <NavigationItem title={x.title} route={x.route} icon={x.icon} childRoutes={x.children} />
              );
            })}
          </Nav>
        </Sidenav.Body>
        <Sidenav.Toggle onToggle={onExpand} />
      </Sidenav>
    </div>
  );
};
```

- appearance - menu style defined by React Suite
- expanded - whether sidebar is expanded
- onOpenChange - navigation opening callback function
- onExpand - navigation expand callback function

 **Final step** in implementing dynamic routing is to define layout in **App.js** and map all routes there in some kind of "container", so content of current route can be properly rendered.

```
export const App = () => {
  const [expanded, setExpand] = React.useState(true);
  const routes = Router.filter((r) => r.title).concat(
    Router.filter((r) => r.children && r.children.length)
      .map((r) => r.children)
      .flat()
  );

  return (
    <>
      <div>
        <Container>
          <Sidebar
            className={'sidebar'}
            width={expanded ? 260 : 56}
            collapsible
          >
            <Navigation
              expanded={expanded}
              onExpand={setExpand}
            />
          </Sidebar>
          <Content>
            <Routes>
              {routes.map((x) => {
                return (
                  <>
                    <Route path={x.route} element={x.component} />
                  </>
                );
              })}
              <Route path={"*"} element={<NotFound />} />
            </Routes>
          </Content>
        </Container>
      </div>
    </>
  );
};
```

## 2. Creating "Not Found" page.

In case user types random route in browser search bar, it would be nice to redirect him to classic "Not Found" page.

![Not found page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/torwnwc1aw2a2dfusc1x.png)
 

What we need to do is create our **NotFound** React component (page).

```
export const NotFound = () => {
    return (
        <Page header={'Content not found!'}>
            <GearIcon spin className={'gear-icon'} />
        </Page>
    )
}
```

**Last thing** we need to do to make sure this works is to add NotFound page to our router in App.js manually. It should be added as last route.

```
<Route path={'*'} element={<NotFound />} /> 
```

## Thank you for reading!