React Router V6 - Must Know Knowledge
React Router V6 - Must Know Knowledge

In this post you will learn what is React Router and how you can use it inside React correctly.

Actually React is considered a library and now a framework this is why we don't have a router inside React at all. This means that we must install some additional library and the most popular solution for React which is not official is called React Router.

Website

As you can see this is the official website with version 6. This router covers all needs that you will have. This is why it doesn't make any sense to look for alternative.

Basic routing

So first of all let's try to install react-router and configure some basic routes. And just for you to know this guide uses react-router v6. It is important because they break quite a lot from major version to major version.

Now we must install our new package

npm install react-router-dom

As you can see we don't install react-router but react-router-dom. We write like this because inside react-router we have quite a lot of routes. As you can see here we have browser-router, hash-router, react-native-router and much more.

Routers

We install a router which is specific for web applications.

Our next step is to wrap our application with a router component.

import { BrowserRouter } from "react-router-dom";

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

With this BrowserRouter we will get access to the router inside all our components.

Here I already create 3 components so we can render them on different pages.

// src/Dashboard.js
const Dashboard = () => {
  return <div>Dashboard</div>;
};

export default Dashboard;
// src/Public.js
const Public = () => {
  return <div>Public</div>;
};

export default Public;
// src/Private.js
const Private = () => {
  return <div>Private</div>;
};

export default Private;

These are just components with some text.

Our next step here is to register some links and routes. Actually routes inside react-router is not registered like routes in some backend framework. For example in backend frameworks you have a file routes and inside you define all possible routes for your application. Previously it worked exactly like this in react-router but not anymore.

Nowadays we define all our routes like components inside our React tree.

const App = () => {
  return (
    <div>
      <h1>Hello monsterlessons</h1>
      <Routes>
        <Route path="/" element={<Dashboard />}>
        </Route>
      </Routes>
    </div>
  );
};

Here we defined a Routes component with a Route component inside. In it we must provide a path and an element.

Let's check if it's working.

Dashboard

As you can see on the home page our Dashboard component was rendered.

Now let's render here one more route.

const App = () => {
  return (
    <div>
      <h1>Hello monsterlessons</h1>
      <Routes>
        <Route path="/" element={<Dashboard />}>
        <Route path="/protected" element={<Protected />}>
        </Route>
      </Routes>
    </div>
  );
};

Now we can jump to /protected and see our component there.

Adding links

But obviously inside our application we want to jump to different routes. And for this we want to register links. For this we must use a component which is called Link.

const App = () => {
  return (
    <div>
      <h1>Hello monsterlessons</h1>
      <Link to="/">Dashboard</Link>
      <Link to="/protected">Protected</Link>
      ...
    </div>
  );
};

Inside a Link we provide a url to to property and a text of the link.

Links

As you can see we rendered 2 links and we can jump between them without page reload and our component is being rerendered

Adding layout

Then next thing that you for sure want to implement is called layout. Typically you want to wrap all your routes with same layout on all pages. For example you have a header, footer and sidebar on all pages. How we can do that?

First of all let's create a layout component.

// src/Layout.js
import { Outlet } from "react-router-dom";

const Layout = () => {
  return (
    <div>
      <h1>Layout</h1>
      <div>
        <Outlet />
      </div>
    </div>
  );
};

export default Layout;

Here we imported an Outlet component inside out layout. It is a placeholder where our component of the route will be rendered. So typically you create sidebar, header, footer in this Layout component and every single route content will be rendered inside the Outlet.

Now we must wrap all our routes with Layout component.

<Routes>
  <Route path="/" element={<Layout />}>
    <Route index element={<Dashboard />} />
    ...
  </Route>
</Routes>

So we just add a new Route for homepage where we render Layout component and all our routes we register as children routes inside of it.

But here is an important thing. Because we provided a path / to our Layout component we can't provide it to our Dashboard that we want to render on the homepage. Instead we add an attribute index to our Dashboard route to define that it is our homepage.

Layout

As you can see in browser each of our routes is wrapped in Layout component.

404 Page

The next important question is how to render 404 page for not existing routes. This is extremely easy to implement.

<Routes>
  <Route path="/" element={<Layout />}>
    ...
    <Route path="*" element={<div>404</div>} />
  </Route>
</Routes>

Inside our Routes we register a new route with path *. It means that for all not existing routes we will render this component. As you can see it is completely possible to render just markup in the element even without creation of component.

If we try to open non existing route now our 404 markup is rendered.

Adding dynamic parameters

Another use case that you will have for sure is dynamic parameters. For example you have a list of articles that you need to render and you have a separate page for every single article where you render our article by slug.

In this case we must register a dynamic route.

<Routes>
  <Route path="/" element={<Layout />}>
    ...
    <Route path="/articles/:slug" element={<Article />}></Route>
  </Route>
</Routes>

Here we have a dynamic url where we get a slug and render an Article component. Now let's create article component and read our dynamic slug there.

import { useParams } from "react-router-dom";

const Article = () => {
  const params = useParams();
  console.log("params", params);
  return <div>Article {params.slug}</div>;
};

export default Article;

As you can see we imported useParams hook from react-router which allow us to render dynamic parameters from the url.

Dynamic param

We successfully rendered a slug from url and can use it later to fetch article from API by a slug.

Getting query-params

Another common use case is to read query parameters from the url inside a page. For this we have a special hook.

import { useParams, useSearchParams } from "react-router-dom";

const Article = () => {
  const params = useParams();
  const [searchParams, setSearchParams] = useSearchParams();
  console.log("params", params, searchParams.get("page"));
  return <div>Article {params.slug}</div>;
};

export default Article;

Here we used useSearchParams which allow us to get specific query params from the url. In this case here we have read page value from the url /articles/foo?page=2.

Programmatic navigation

One more thing that you will for sure need is programmatic navigation. It means that we want to trigger navigation to another route not by link but from our code.

Let's create a button which will navigate us to other route.

const App = () => {
  const navigate = useNavigate();
  return (
    <div>
      <h1>Hello monsterlessons</h1>
      <button onClick={() => navigate('/protected')}>Go to protected</button>
      ...
    </div>
  );
};

Here we imported useNavigate hook from react-router. Secondly we used it when we click on the button to jump to protected page. It is an extremely important feature if you want to redirect a user to other page after registration or authentication.

Authentication

Here is the last thing that I want to show you which is the most interesting. How we can protect our routes from being accessed by anonymous user?

Let's say that we want to prevent access to our /protected page if we are not logged in. Typically you want to have a token we be authorized and make an API request but it is not related at all to react-router. Here I will just check if we have a token in local storage to give access to that page.

// src/Auth.js
import { Navigate } from "react-router-dom";

const Auth = ({ children }) => {
  const token = localStorage.getItem("token");

  if (!token) {
    return <Navigate to="/" />;
  }
  return children;
};

export default Auth;

Here we created new Auth component which will wrap our protected routes. It reads a token from local storage and redirects a user to homepage it he is not logged in. In other case it simply renders child route.

Now we need to wrap our protected route with Auth.

<Routes>
  <Route path="/" element={<Layout />}>
    <Route
      path="/protected"
      element={
        <Auth>
          <Protected />
        </Auth>
      }
    />
  </Route>
  ...
</Routes>

Here we simply wrapped our element that we render with Auth component.

As you can see in browser we can't access protected routes anymore because without token we are redirected to the home page. But if we set any value inside token key then we can access it.

And actually if you are interested to learn how to build React project from start to the end make sure to check my React for beginners course.

📚 Source code of what we've done