React Protected Routes - How to Make React Router Protected Route
React Protected Routes - How to Make React Router Protected Route

In this post you will learn how to protect your routes inside React Router in your React application.

Just from the start I must tell you that it is impossible to protect your routes on the client side. Anybody can access your Javascript, execute code and access your page.

This is why it is extremely important to protect your data on the backend and you don't allow person who is not logged it to access your API.

With that being cleared let's look on our application.

Initial project

As you can see here I already prepared an application with 2 pages. We have here Dashboard and Protected routes. What we want to do is access this /protected route only when our user is logged it.

Let's look what we have inside our application.

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

Inside main.js we wrapped everything with BrowserRouter because we are using react-router which is the most popular solution for routing inside React.

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

Inside our App component we have 2 links and 2 routes to our Dashboard and Protected components. Inside our components we don't have anything at all but just text.

The way how we will protect our routes doesn't have anything to do with react-router. The main idea is that we can wrap our components that we want to protect with additional component which will check if we can access a component.

This is how the usage will look like.

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

We just wrapped Protected with Auth component that we will create in a second.

Sync protection

Now let's create our Auth component.

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

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

  return <Navigate to="/" />;
};

Here we created an example of synchronous check. We try to get a value from localStorage and if it is there we render our child component. If it is not we redirect a user to homepage.

As you can see in browser we can't access our /protected without a token. If we add a token to localStorage with any value it will work.

Async protection

But it is not enough for real application as we typically fetch current user from the API which means our check must be asynchronous and we must wait until we have a user before we decide if we render a component.

const Auth = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(undefined);

  useEffect(() => {
    setTimeout(() => {
      if (localStorage.getItem("token")) {
        setCurrentUser({ id: "1", name: "foo" });
      } else {
        setCurrentUser(null);
      }
    }, 2000);
  }, []);

  if (currentUser === undefined) {
    return null;
  }

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

  return children;
};

Here is an example of async check. First of all we created a state for currentUser. Secondly we try to load a user on initialize of Auth component. In real application you would want to do an API call here but for simplicity I wrote localStorage check inside setTimeout to simulate long API request.

If we are logged in we set an object with data to currentUser in other case we set null. Additionally we return null in this component if we don't have currentUser yet. It means that it won't render anything. If we got currentUser information but it is null then we redirect a user to homepage. And if we are logged in we render the child component.

As you can see in browser we have a delay before we see the content of our private route. This is our timeout while we are getting a user from the API.

Now we built a reusable component which can be applied to any place.

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

📚 Source code of what we've done