Navigation in React App using React Router (v6)
Tags: react
Published: 2021-11-10 07:05:52

Most of the application you develop will have multiple pages and you would require to have a separate URL for each one of them.
React cannot handle routing on its own. There are many libraries like [react router](https://reactrouter.com/),[reach router](https://reach.tech/router/), [react navigation](https://reactnavigation.org/docs/web-support/) etc to handle navigation in react. In this post we will see how we can use react router to handle navigation in react apps.

## Project setup

Create a new react app using the following command:

```bash
npx create-react-app react-router-tutorial
```

Now install the react-router-dom and history package:

```bash
yarn add react-router-dom@next history
```

## Basic Routing

Now in `index.js` wrap the `App` component with the `BrowserRouter` component, which can be imported from the `react-router-dom` package that we just installed.

```jsx
import React from "react"
import ReactDOM from "react-dom"
import { BrowserRouter } from "react-router-dom"
import App from "./App"
import "./index.css"

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById("root")
)
```

It is necessary to wrap any routes or links created using react router with `Router` component (in our case `BrowserRouter`). So we wrap the whole application inside `BrowserRouter`.

`BrowserRouter` is a variant of `Router` which uses the [HTML5 history API](https://developer.mozilla.org/en-US/docs/Web/API/History_API), which helps in maintaining the browser history.

Now update `App.js` with the following code:

```jsx
import React from "react"
import { Routes, Route, Link } from "react-router-dom"

function App() {
  return (
    <div className="App">
      <nav>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="dashboard">Dashboard</Link>
          </li>
          <li>
            <Link to="about">About</Link>
          </li>
        </ul>
      </nav>
      <div className="main">
        {/* Define all the routes */}
        <Routes>
          <Route path="/" element={<Home />}></Route>
          <Route path="about" element={<About />}></Route>
          <Route path="*" element={<NotFound />}></Route>
        </Routes>
      </div>
    </div>
  )
}

export const Home = () => {
  return <div>You are in Home page</div>
}
export const About = () => {
  return <div>This is the page where you put details about yourself</div>
}
export const NotFound = () => {
  return <div>This is a 404 page</div>
}

export default App
```

In the above code:

- We are having a few navigation links, which are defined using the `Link` component. The `to` property will determine the URL to which the user needs to be navigated.

- The component that needs to be rendered when the user is navigated to a particular path is defined by the `element` property in the `Route` component. For example, `/about` route will render the `About` component.

- If you want to display a 404 page when the path does not match with any of the routes then you can define a route with path as `*`.

- Finally, we need to wrap all the `Route` components inside the `Routes` component, which is again exported from `react-router-dom`.

- The order of `Route` components does not matter. React router will match the best route irrespective of order.

Before running our app, let's add some basic styling to `index.css`:

```css
body {
  margin: 0 auto;
  max-width: 900px;
}
nav ul {
  display: flex;
  list-style-type: none;
  margin: 0;
  padding: 0;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}

nav a {
  text-decoration: none;
  display: inline-block;
  padding: 1rem;
}
.main {
  padding: 1rem;
}
```

Now run the application and navigate through the links and you should be able to see the appropriate components being rendered.

![Basic Routing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7fesjwmoh0lg9vem20dc.png)

You would have observed that /dashboard lands in 404 page. This is because we do not have a dashboard route defined yet. Also, you would see that we have created the `Home` and `About` component within `App.js`, we can have the components defined in their own files. So let's create `Dashboard` component inside `Dashboard.js` file:

```jsx
import React from "react"

const Dashboard = () => {
  return <div>Dashboard</div>
}

export default Dashboard
```

Now import it in `App.js` and add include it in the list of Routes:

```jsx
//...

import Dashboard from "./Dashboard"

function App() {
  return (
    <div className="App">
      <nav>{/* ... */}</nav>
      <div className="main">
        <Routes>
          <Route path="/" element={<Home />}></Route>
          <Route path="about" element={<About />}></Route>
          <Route path="dashboard" element={<Dashboard />}></Route>
          <Route path="*" element={<NotFound />}></Route>
        </Routes>
      </div>
    </div>
  )
}
//...
```

Now you should have the dashboard route working.

## Active Class Name

Since all of our links are navigation links it will be nice to highlight the link which is currently active. For this purpose we have a special component called `NavLink` component.

```jsx
//...
import { Routes, Route, NavLink as Link } from "react-router-dom"

function App() {
  return (
    <div className="App">
      <nav>
        <ul>
          <li>
            <Link to="/" activeClassName="active">
              Home
            </Link>
          </li>
          <li>
            <Link to="dashboard" activeClassName="active">
              Dashboard
            </Link>
          </li>
          <li>
            <Link to="about" activeClassName="active">
              About
            </Link>
          </li>
        </ul>
      </nav>
      <div className="main">
        <Routes>{/* ... */}</Routes>
      </div>
    </div>
  )
}

//...
export default App
```

In the above code, you will see that we are importing `NavLink` as the `Link` component and also we have added `activeClassName` property with a value of 'active' to the `Link` component. The `active` class will be added to the anchor, whichever matches the current URL.

Now to differentiate the active link, let's add some css:

```css
/* ... */
nav a.active {
  background-color: #eee;
}
```

Now if you run the application, you will see the active link having a different background color:

![Active Class Name](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9s03op6p6jcikid3qf69.png)

Now you will see that we have a problem! The Home link is highlighted every time. This is because we have given `/` as the path for the Home link and all other page links have `/` in them.
So react router does a contains match to provide the active class name. We can fix this by providing another parameter called `end` to our link. `end` property tells react router to match the exact path and add active class name.

```jsx
<Link to="/" activeClassName="active" end>
  Home
</Link>
```

Now you should have the active links working as expected:

![Active Class Name End](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6m9phknj5unbwy4oerzx.png)

## Nested Routes

In case you want to have pages inside the dashboard page, you can have routes configured inside the `Dashboard` component, thus by nesting the routes under the routes defined in `App.js`.

Similar to what we have done in `App.js`, set up 3 routes inside `Dashboard.js` as shown below:

```jsx
import React from "react"
import { Routes, Link, Route } from "react-router-dom"

const Dashboard = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="">Profile</Link>
        </li>
        <li>
          <Link to="orders">Orders</Link>
        </li>
        <li>
          <Link to="quotes">Quotes</Link>
        </li>
      </ul>
      <div className="dashboard">
        <Routes>
          <Route path="/" element={<Profile />}></Route>
          <Route path="orders" element={<Orders />}></Route>
          <Route path="quotes" element={<Quotes />}></Route>
        </Routes>
      </div>
    </div>
  )
}

export const Profile = () => {
  return <h2>Profile</h2>
}
export const Orders = () => {
  return <h2>Orders</h2>
}
export const Quotes = () => {
  return <h2>Quotes</h2>
}

export default Dashboard
```

Now we need to update the dashboard route in `App.js` with a `/*` in the end so that it matches all the routes under it:

```jsx
<Route path="dashboard/*" element={<Dashboard />}></Route>
```

Also, let's add some styling:

```css
/* ... */
.main ul {
  display: flex;
  list-style-type: none;
  margin: 0;
  padding: 0;
}
.main ul li {
  margin-right: 1rem;
}

.dashboard {
  padding: 1rem 0;
}
```

Now if you run the app, you will see:

- Orders and quotes pages having a URLs `/dashboard/orders` and `/dashboard/quotes`,
  this is because we nested these routes inside the dashboard route.
- We have given a path of `"/"` to `Profile` component, so that it loads by default when the user hits `/dashboard` route.

![Nested Routes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kgdft7r44bmayuk58b9a.png)

## Passing URL parameters to a route

The next thing we will see is how we can pass URL parameters to a route:

```jsx
import React from "react"
import { Routes, Link, Route, useParams } from "react-router-dom"

const Dashboard = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="">Profile</Link>
        </li>
        <li>
          <Link to="orders">Orders</Link>
        </li>
        <li>
          <Link to="quotes">Quotes</Link>
        </li>
      </ul>
      <div className="dashboard">
        <Routes>
          <Route path="/" element={<Profile />}></Route>
          <Route path="orders" element={<Orders />}></Route>
          <Route path="quotes" element={<Quotes />}></Route>
          <Route path="order_details/:orderId" element={<OrderDetails />} />
        </Routes>
      </div>
    </div>
  )
}

export const Profile = () => {
  return <h2>Profile</h2>
}
export const Orders = () => {
  const orderIds = ["10001", "10002", "10003"]
  return (
    <>
      <h2>Orders</h2>
      <ul className="orders">
        {/* Loop through the orders array and display link to order details */}
        {orderIds.map(orderId => {
          return (
            <li key={orderId}>
              <Link to={`/dashboard/order_details/${orderId}`}>
                View Order {orderId}
              </Link>
            </li>
          )
        })}
      </ul>
    </>
  )
}
export const Quotes = () => {
  return <h2>Quotes</h2>
}
export const OrderDetails = () => {
  const params = useParams()

  return <h2>Details of order {params.orderId}</h2>
}

export default Dashboard
```

In the above code:

- We are looping through a list of order ids and creating a link to `order_details` route and we are appending it with the order id.
- To catch the route dynamically, we add `:orderId` to the route configuration in `Orders` component.
- In the OrderDetails component,
  we make use of the `useParams` hook that can be imported from the `react-router-dom` to retrieve the value of `orderId` and display it.

Before testing the application let's add some css:

```css
/* ... */
ul.orders {
  flex-direction: column;
  border: 1px solid;
  padding: 0.5rem;
}
.orders li {
  padding: 0.5rem 0;
}
ul.invoices {
  flex-direction: column;
  border: 1px solid;
  padding: 0.5rem;
}
.invoices li {
  padding: 0.5rem 0;
}
```

Now if you run run the app, you will see that we can retrieve the `orderId` parameter from the URL:

![URL Params](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s9pe5mvks5o06rwzqqdc.png)

## Navigating programmatically to a route

If you want to perform navigation on certain user action, say on click of a button, react router provides us with a hook for it called `useNavigate`. Now we have order details page, we can add a link back to orders page and implement it using `useNavigate`.

```jsx
//...

import { Routes, Link, Route, useParams, useNavigate } from "react-router-dom"

//...

export const OrderDetails = () => {
  const params = useParams()
  const navigate = useNavigate()

  const onBackClick = e => {
    e.preventDefault()
    // navigate(-1);
    navigate("/dashboard/orders")
  }

  return (
    <>
      <h2>Details of order {params.orderId}</h2>
      <a href="#" onClick={onBackClick}>
        Back to Orders
      </a>
    </>
  )
}
```

We can pass the absolute path where the user needs to be navigated or call `navigate(-1)` to go back a page.

![Navigating programmatically](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v293q3yqc3b1dnrfi69c.gif)

## Configuring Routes as an Object

It is not necessary to configure the routes as a component and wrap it inside the `Routes` component. We can specify the route configuration in a JSON object as well. This will help when we have dynamic routes and we get the route details from an API call.

Create a new component called `RouteAsObj` with the below code

```jsx
import React from "react"
import { useRoutes, Outlet } from "react-router"
import { Link } from "react-router-dom"

const RouteAsObj = () => {
  let element = useRoutes([
    { path: "/", element: <Route1 /> },
    { path: "route2", element: <Route2 /> },
    {
      path: "route3",
      element: <Route3 />,
      // children can be used to configure nested routes
      children: [
        { path: "child1", element: <Child1 /> },
        { path: "child2", element: <Child2 /> },
      ],
    },
    { path: "*", element: <NotFound /> },
  ])

  return (
    <div>
      <ul>
        <li>
          <Link to="">Route1</Link>
        </li>
        <li>
          <Link to="route2">Route2</Link>
        </li>
        <li>
          <Link to="route3">Route3</Link>
        </li>
      </ul>
      {element}
    </div>
  )
}

const Route1 = () => <h1>Route1</h1>
const Route2 = () => <h1>Route2</h1>
const Route3 = () => {
  return (
    <div>
      <h1>Route3</h1>
      <ul>
        <li>
          <Link to="child1">Child1</Link>
        </li>
        <li>
          <Link to="child2">Child2</Link>
        </li>
      </ul>
      <Outlet />
    </div>
  )
}
const Child1 = () => <h2>Child1</h2>
const Child2 = () => <h2>Child2</h2>
const NotFound = () => <h1>NotFound</h1>

export default RouteAsObj
```

In the above code:

- We are creating the components similar to previous examples.
  The difference is that we are making use of `useRoutes` hook and passing our route configuration to it.
  The `useRoutes` either returns a valid react component, which we have embedded in the component as `element`.
- Also, you could see that we have added `<Outlet />` component inside the `Route3`.
  This will help in rendering the matching child route, when the routes are nested.

Now let's include the route in the `App.js`

```jsx
import React from "react"
import { Routes, Route, NavLink as Link } from "react-router-dom"
import Dashboard from "./Dashboard"
import RouteAsObj from "./RouteAsObj"

function App() {
  return (
    <div className="App">
      <nav>
        <ul>
          <li>
            <Link to="/" activeClassName="active" end>
              Home
            </Link>
          </li>
          <li>
            <Link to="dashboard" activeClassName="active">
              Dashboard
            </Link>
          </li>
          <li>
            <Link to="about" activeClassName="active">
              About
            </Link>
          </li>
          <li>
            <Link to="/object_route" activeClassName="active">
              Route as Object
            </Link>
          </li>
        </ul>
      </nav>
      <div className="main">
        <Routes>
          <Route path="/" element={<Home />}></Route>
          <Route path="about" element={<About />}></Route>
          <Route path="dashboard/*" element={<Dashboard />}></Route>
          <Route path="object_route/*" element={<RouteAsObj />}></Route>
          <Route path="*" element={<NotFound />}></Route>
        </Routes>
      </div>
    </div>
  )
}

//...
export default App
```

Now if you run the app you would see the routes working as expected:

![Routes as Object](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/isvsytcvkl88ae82dks7.png)

## Query parameters

You might encounter scenarios where you need to extract the query parameters. This can be done by using the `useLocation` hook provided by react router.

Let's create a Search component with a search form:

```jsx
import React, { useRef } from "react"
import { useLocation, useNavigate } from "react-router-dom"

function useQuery() {
  // Use the URLSearchParams API to extract the query parameters
  // useLocation().search will have the query parameters eg: ?foo=bar&a=b
  return new URLSearchParams(useLocation().search)
}

const Search = () => {
  const query = useQuery()

  const term = query.get("term")

  const inputRef = useRef(null)
  const navigate = useNavigate()

  const formSubmitHandler = e => {
    //prevent the default form submission
    e.preventDefault()

    //extract search term using refs.
    const searchValue = inputRef.current.value
    navigate(`?term=${searchValue}`)
  }

  return (
    <div>
      <form action="" onSubmit={formSubmitHandler}>
        <input type="text" name="term" ref={inputRef} />
        <input type="submit" value="Search" />
        {/* Display the search term if it is present */}
        {term && <h2>Results for '{term}'</h2>}
      </form>
    </div>
  )
}

export default Search
```

Here we are using yet another hook called `useLocation`, which will return the URL details. The `search` property within it will have the query string. We have made use of [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
API to extract the query parameters. We have included this in a custom hook called `useQuery`, which is later used to extract the search term using the `query.get("term")` call inside Search component.

Now let's include a route to search page in the `App` component:

```jsx
//...
import Search from "./Search"

function App() {
  return (
    <div className="App">
      <nav>
        <ul>
          {/* Other Links */}
          <li>
            <Link to="/search" activeClassName="active">
              Search
            </Link>
          </li>
        </ul>
      </nav>
      <div className="main">
        <Routes>
          {/* Other Routes */}
          <Route path="search" element={<Search />}></Route>
          <Route path="*" element={<NotFound />}></Route>
        </Routes>
      </div>
    </div>
  )
}

//...
```

Now if we run the app and search for something, we will see that it is displaying the searched term:

![Query Params](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iq7uqgyrke10duegbvav.png)

## Authenticated Routes

You will have certain pages in your application that needs to be accessed only by logged in users. We can secure such routes by writing a wrapper around the `Route` component.

Before writing the Route component, let's create a fake authentication function:

```js
export const fakeAuth = {
  isAuthenticated: false,
  login(callBack) {
    fakeAuth.isAuthenticated = true
    callBack()
  },
  logout(callBack) {
    fakeAuth.isAuthenticated = false
    callBack()
  },
}
```

Here we have `isAuthenticated` property, which will be set to `true` and `false` by the `login` and `logout` functions. These functions will also call the passed callback function.

Now let's create a protected page, which needs to be secured from unauthorized access.

```jsx
import React from "react"
import { fakeAuth } from "./fakeAuth"
import { useNavigate } from "react-router-dom"

const ProtectedPage = ({ x }) => {
  const navigate = useNavigate()
  return (
    <div>
      <p>You are logged in. Welcome to protected page! Value of x is {x}</p>
      <button
        onClick={() => {
          fakeAuth.logout(() =>
            navigate("/login", { state: { from: { pathname: "/protected" } } })
          )
        }}
      >
        Sign out
      </button>
    </div>
  )
}

export default ProtectedPage
```

Here we are showing a welcome message and a logout button, on click of which user will be redirected to login page. Notice that we are passing the `state` as the second argument to `navigate` function, this will be used to redirect the user to `/protected` route after login.

Now let's create the login page. Here we are having a login button, on click of which we will call the fake login function and redirect the user to the pathname passed in the state.
In our case it will have the value as `/protected`.

```jsx
import React from "react"
import { useNavigate, useLocation } from "react-router-dom"
import { fakeAuth } from "./fakeAuth"

function LoginPage() {
  let navigate = useNavigate()
  let location = useLocation()

  let { from } = location.state || { from: { pathname: "/" } }
  let login = () => {
    fakeAuth.login(() => {
      navigate(from)
    })
  }

  return (
    <div>
      <p>You must log in to view the page at {from.pathname}</p>
      <button onClick={login}>Log in</button>
    </div>
  )
}

export default LoginPage
```

Now let's create the private route we mentioned earlier:

```jsx
import React from "react"
import { Navigate, useLocation } from "react-router-dom"
import { fakeAuth } from "./fakeAuth"

/**
 * A wrapper around the element which checks if the user is authenticated
 * If authenticated, renders the passed element
 * If not authenticated, redirects the user to Login page.
 */
const PrivateElement = ({ children }) => {
  let location = useLocation()
  return fakeAuth.isAuthenticated ? (
    children
  ) : (
    <Navigate to="/login" state={{ from: location }} />
  )
}

export default PrivateElement
```

As you can see, the above route is a wrapper around the `Route` component to check if the user is authenticated. If the user is authenticated then it renders the passed component otherwise
redirect the user to login page using the `Navigate` component.

`Navigate` component is another way of redirecting the user to another page. We are also passing the from location to the login route so that user can be redirected back to the actual route once they log in.

Now let's wire up everything to `App.js`:

```jsx
import React from "react"
import { NavLink as Link, Route, Routes } from "react-router-dom"
import Dashboard from "./Dashboard"
import LoginPage from "./LoginPage"
import PrivateRoute from "./PrivateRoute"
import ProtectedPage from "./ProtectedPage"
import RouteAsObj from "./RouteAsObj"
import Search from "./Search"

function App() {
  return (
    <div className="App">
      <nav>
        <ul>
          <li>
            <Link to="/" activeClassName="active" end>
              Home
            </Link>
          </li>
          <li>
            <Link to="/dashboard" activeClassName="active">
              Dashboard
            </Link>
          </li>
          <li>
            <Link to="/about" activeClassName="active">
              About
            </Link>
          </li>
          <li>
            <Link to="/object_route" activeClassName="active">
              Route as Object
            </Link>
          </li>
          <li>
            <Link to="/search" activeClassName="active">
              Search
            </Link>
          </li>
          <li>
            <Link to="/public" activeClassName="active">
              Public Page
            </Link>
          </li>
          <li>
            <Link to="/protected" activeClassName="active">
              Protected Page
            </Link>
          </li>
        </ul>
      </nav>
      <div className="main">
        <Routes>
          <Route path="/" element={<Home />}></Route>

          <Route path="about" element={<About />}></Route>
          <Route path="dashboard/*" element={<Dashboard />}></Route>
          <Route path="object_route/*" element={<RouteAsObj />}></Route>
          <Route path="search" element={<Search />}></Route>
          <Route path="public" element={<PublicPage />}></Route>
          <Route
            path="protected"
            element={
              <PrivateRoute>
                <ProtectedPage x={1} />
              </PrivateRoute>
            }
          ></Route>
          <Route path="login" element={<LoginPage />}></Route>
          <Route path="*" element={<NotFound />}></Route>
        </Routes>
      </div>
    </div>
  )
}

export const Home = () => {
  return <div>You are in Home page</div>
}
export const About = () => {
  return <div>This is the page where you put details about yourself</div>
}
export const PublicPage = () => {
  return <div>This page can be accessed by anyone</div>
}
export const NotFound = () => {
  return <div>This is a 404 page</div>
}

export default App
```

If you run the application now:

![Authenticated Routes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/per8txdnpyniz6ptxzuv.gif)

## Code Splitting

When we have lot of pages in out application, we will end up having lot of code. We don't want our user to download all the code when they just load the home page. In order to package code of different routes to separate chunks, along with react router we can make use of [loadable components](https://loadable-components.com/), which takes advantage of dynamic imports.

To start with, install the following package:

```bash
yarn add @loadable/component
```

In the `App.js`, let's import the `Dashboard` component dynamically and pass it to the `loadable` function. It also accepts a second argument, which has a `fallback` property, which needs a component name as the argument. This fallback component will be rendered while the js code is being downloaded. Also, if the component js fails to load, the fallback component will remain being shown.

```jsx
import loadable from "@loadable/component"
import React from "react"
import { NavLink as Link, Route, Routes } from "react-router-dom"
import LoginPage from "./LoginPage"
import PrivateRoute from "./PrivateRoute"
import ProtectedPage from "./ProtectedPage"
import RouteAsObj from "./RouteAsObj"
import Search from "./Search"

const Loading = () => {
  return <div>Loading...</div>
}

const Dashboard = loadable(() => import("./Dashboard.js"), {
  fallback: <Loading />,
})

function App() {
  return (
    <div className="App">
      <nav>
        <ul>
          <li>
            <Link to="/" activeClassName="active" end>
              Home
            </Link>
          </li>
          <li>
            <Link to="/dashboard" activeClassName="active">
              Dashboard
            </Link>
          </li>
          <li>
            <Link to="/about" activeClassName="active">
              About
            </Link>
          </li>
          <li>
            <Link to="/object_route" activeClassName="active">
              Route as Object
            </Link>
          </li>
          <li>
            <Link to="/search" activeClassName="active">
              Search
            </Link>
          </li>
          <li>
            <Link to="/public" activeClassName="active">
              Public Page
            </Link>
          </li>
          <li>
            <Link to="/protected" activeClassName="active">
              Protected Page
            </Link>
          </li>
        </ul>
      </nav>
      <div className="main">
        <Routes>
          <Route path="/" element={<Home />}></Route>

          <Route path="about" element={<About />}></Route>
          <Route path="dashboard/*" element={<Dashboard />}></Route>
          <Route path="object_route/*" element={<RouteAsObj />}></Route>
          <Route path="search" element={<Search />}></Route>
          <Route path="public" element={<PublicPage />}></Route>
          <Route
            path="protected"
            element={
              <PrivateRoute>
                <ProtectedPage x={1} />
              </PrivateRoute>
            }
          ></Route>
          <Route path="login" element={<LoginPage />}></Route>
          <Route path="*" element={<NotFound />}></Route>
        </Routes>
      </div>
    </div>
  )
}

export const Home = () => {
  return <div>You are in Home page</div>
}
export const About = () => {
  return <div>This is the page where you put details about yourself</div>
}
export const PublicPage = () => {
  return <div>This page can be accessed by anyone</div>
}
export const NotFound = () => {
  return <div>This is a 404 page</div>
}

export default App
```

Now if you open the browsers network tab and load the home page, you would see a bunch of files being loaded:

![Code Splitting Home](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xdvi35xky67jns0pnmki.png)

Now clear the network logs and click on dashboard link and you will observe a new js file being loaded, which is responsible for rendering the contents inside dashboard:

![Code Splitting Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y299v8sniscyhzy2xcf2.png)

## Index Routes

Index routes can be used when there is a list of routes generated programmatically and you need to display a fallback text or component when the parameter is not provided.

That is, if you have routes like `/invoices/50001`, `/invoices/50002`, so on and, if the user visits `/invoices` you may need to display them a message telling them to select an invoice.

Create a file named `Invoices.js` with the following code. This is similar to the order details route we created earlier.

```jsx
import React from "react"
import { Link, Outlet, useParams } from "react-router-dom"

export const Invoices = () => {
  const invoiceIds = ["50001", "50002", "50003"]
  return (
    <>
      <h2>Invoices</h2>
      <ul className="invoices">
        {invoiceIds.map(invoiceId => {
          return (
            <li key={invoiceId}>
              <Link to={`/invoices/${invoiceId}`}>
                View Invoice {invoiceId}
              </Link>
            </li>
          )
        })}
        <Outlet />
      </ul>
    </>
  )
}

export const Invoice = () => {
  const params = useParams()

  return (
    <>
      <h2>Details of invoice {params.invoiceId}</h2>
    </>
  )
}

export default Invoices
```

In `App.js` we can make use of the nested routes to specify the index route.

```jsx
import loadable from "@loadable/component"
import React from "react"
import { NavLink as Link, Route, Routes } from "react-router-dom"
import Invoices, { Invoice } from "./Invoices"
import LoginPage from "./LoginPage"
import PrivateRoute from "./PrivateRoute"
import ProtectedPage from "./ProtectedPage"
import RouteAsObj from "./RouteAsObj"
import Search from "./Search"

const Loading = () => {
  return <div>Loading...</div>
}

const Dashboard = loadable(() => import("./Dashboard.js"), {
  fallback: <Loading />,
})

function App() {
  return (
    <div className="App">
      <nav>
        <ul>
          <li>
            <Link to="/" activeClassName="active" end>
              Home
            </Link>
          </li>
          <li>
            <Link to="/dashboard" activeClassName="active">
              Dashboard
            </Link>
          </li>
          <li>
            <Link to="/about" activeClassName="active">
              About
            </Link>
          </li>
          <li>
            <Link to="/object_route" activeClassName="active">
              Route as Object
            </Link>
          </li>
          <li>
            <Link to="/search" activeClassName="active">
              Search
            </Link>
          </li>
          <li>
            <Link to="/public" activeClassName="active">
              Public Page
            </Link>
          </li>
          <li>
            <Link to="/protected" activeClassName="active">
              Protected Page
            </Link>
          </li>
          <li>
            <Link to="/invoices" activeClassName="active">
              Invoices
            </Link>
          </li>
        </ul>
      </nav>
      <div className="main">
        <Routes>
          <Route path="/" element={<Home />}></Route>

          <Route path="about" element={<About />}></Route>
          <Route path="dashboard/*" element={<Dashboard />}></Route>
          <Route path="object_route/*" element={<RouteAsObj />}></Route>
          <Route path="search" element={<Search />}></Route>
          <Route path="public" element={<PublicPage />}></Route>
          <Route
            path="protected"
            element={
              <PrivateRoute>
                <ProtectedPage x={1} />
              </PrivateRoute>
            }
          ></Route>
          <Route path="login" element={<LoginPage />}></Route>
          <Route path="invoices" element={<Invoices />}>
            <Route
              index
              element={<p>Please select an invoice above</p>}
            ></Route>
            <Route path=":invoiceId" element={<Invoice />} />
          </Route>

          <Route path="*" element={<NotFound />} />
        </Routes>
      </div>
    </div>
  )
}

export const Home = () => {
  return <div>You are in Home page</div>
}
export const About = () => {
  return <div>This is the page where you put details about yourself</div>
}
export const PublicPage = () => {
  return <div>This page can be accessed by anyone</div>
}
export const NotFound = () => {
  return <div>This is a 404 page</div>
}

export default App
```

Now if you run and visit the `/invoices` route, you will see the fallback text displayed:

![index routes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/si84sbikpxxsgripzrr4.png)

If you click on one of the invoice links, you will be navigated to the details page:

![invoice details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4oma5v2x4h2ipsmvnj4x.png)

## Source code and Demo

You can view the complete [source code here](https://github.com/collegewap/react-router-tutorial) and a [demo here](https://react-router-tutorial.vercel.app/).