Handling protected routes with React Router
Today, we'll look at how to handle protected routes (also known as private routes) using React Router and the Context API.
Table of contents
What are protected routes?
Protected routes prevent unauthenticated and unauthorised users from accessing certain parts of our application.
Please be aware that this is not a way to secure your application (more on this next).
A note about security
It is important to note that implementing protected routes is not a way to secure your application, but a way to control what the client sees when they try to access certain routes. Requests to your back-end should be secured with a form of authentication.
As our routing is handled on the client side, it is possible for somebody to spoof the authentication state we are about to implement and access the protected route. This is why interactions between your client and server must be secured.
Let's say somebody manages to access our dashboard without actually being authenticated. This isn't an issue as none of the requests to get any sensitive data will succeed as they will lack the authentication required.
Implementation
Context
We are going to use the Context API to provide our authentication state to the relevant components.
Let's take a look at what that might look like:
1import React, { useEffect, useState, createContext } from "react";23const AuthenticationContext = createContext();45const AuthenticationProvider = ({ children }) => {6 const [authenticating, setAuthenticating] = useState(true);7 const [user, setUser] = useState(null);89 useEffect(() => {10 // Authenticate the user and set state...11 }, []);1213 return <AuthenticationContext.Provider value={{ authenticating, user }}>{children}</AuthenticationContext.Provider>;14};1516export { AuthenticationContext, AuthenticationProvider };
The actual logic has been omitted from this example as it will likely differ to your implementation. A common pattern is to have an effect which runs and makes a request to your back-end which will return whether the user is authenticated or not.
For the purposes of this tutorial, let's mock this and simply immediately authenticate ourselves and set our authenticating state to false
.
1useEffect(() => {2 setUser({ id: 0, name: "Louis Young" });34 setAuthenticating(false);5}, []);
Great. Now we can consume this state in the relevant components.
Protected route component
Now, we want to build a component which allows us to interface with it similarly to how we do with the Route
component from React Router.
Let's see what that looks like:
1import React, { useContext } from "react";23import { Redirect, Route } from "react-router-dom";45import { AuthenticationContext } from "../../context/AuthenticationContext";67const ProtectedRoute = ({ component: Component, path, exact }) => {8 const { authenticating, user } = useContext(AuthenticationContext);910 if (authenticating) {11 return <p>Authenticating...</p>;12 }1314 return user ? <Route path={path} exact={exact} component={Component} /> : <Redirect to="/" />;15};1617export default ProtectedRoute;
Now, let's step through this code:
- We import our components and context
- We define a
ProtectedRoute
component which accepts similar props to the standardRoute
component - We consume our context, and if we are authenticating (loading) then we show a loading state
- We then conditionally render our route, or a redirect depending on state from our context provider
Note: You can spread any additional props on to the
Route
component should you need to.
This is a very simple implementation, but it works. Let's see how we'd use our new component.
Usage
We can use our new ProtectedRoute
component in the same way we would use the Route
component from React Router.
Here is an example of this:
1<Route path="/" component={Home} exact />2<ProtectedRoute path="/dashboard" component={Dashboard} />
You can see that both route components offer a similar API, so if you're familiar with the standard Route
component then there's no learning curve.
Example
Here's an example application showing all of the above sections glued together.
Note: This examples uses a
setTimeout
of 1 second to mock an API request.
Conclusion
This is a minimal example for the purposes of this tutorial, but this can be easily extended to meet your requirements.
Hopefully this article helped you to handle protected routes in your React applications and reminded you of good client/server security practices.