Education
Developer Advocate
Last updated onFeb 24, 2024
Last updated onFeb 24, 2024
Ever found yourself in a maze, trying to figure out how to secure your routes in a React application? Well, you're not alone. React Router authentication is a crucial aspect of any React application that requires user login functionality. It's like the bouncer at the club's entrance, ensuring that only authenticated users get to access certain parts of your app.
In this post, we'll be exploring the nitty-gritty of React Router authentication. We'll cover everything from setting up a new React project, creating a login page, and implementing user authentication, to setting up protected routes. By the end, you'll be well-equipped to protect your routes like a pro.
Before we dive into the world of React Router authentication, we need to set up a new React project. If you've already got a project up and running, feel free to skip this section. For those starting from scratch, here's how you can create a new React project.
First, you need to have Node.js and npm installed on your system. If you haven't installed them yet, head over to the official Node.js website and download the latest stable version. npm comes bundled with Node.js, so you don't need to install it separately.
Once you have Node.js and npm installed, you can create a new React project using the following command:
1 npx create-react-app react-router-auth 2
This command will create a new directory called react-router-auth and set up a new React project inside it.
Next, navigate into your new React project using the following command:
1 cd react-router-auth 2
Now, it's time to install React Router. React Router is not included in the create-react-app boilerplate, so we need to install it manually. You can do this using the following command:
1 npm install react-router-dom 2
And voila! You've successfully set up a new React project and installed React Router.
Now that we have our React project up and running with React Router installed, let's take a moment to understand what React Router Dom is and the core components it provides.
React Router Dom is a powerful routing library for React applications. It allows you to create multiple routes in your application and render different components based on the current URL. This is essential for creating Single-Page Applications (SPAs) where all the routing is handled on the client side.
Here are some of the core components provided by React Router Dom:
Let's see how we can use these components in our React application. Here's a simple code snippet:
1 import React from 'react'; 2 import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom'; 3 4 export default function App() { 5 return ( 6 <Router> 7 <div> 8 <nav> 9 <ul> 10 <li><Link to="/">Home</Link></li> 11 <li><Link to="/about">About</Link></li> 12 </ul> 13 </nav> 14 <Switch> 15 <Route path="/about"><About /></Route> 16 <Route path="/"><Home /></Route> 17 </Switch> 18 </div> 19 </Router> 20 ); 21 } 22 23 function Home() { 24 return <h2>Home</h2>; 25 } 26 27 function About() { 28 return <h2>About</h2>; 29 } 30
In the above code snippet, we have two routes - the home route ("/") and the about route ("/about"). When you navigate to "/", the Home component is rendered, and when you navigate to "/about", the About component is rendered.
Alright, let's get our hands dirty and start building the login page. This is where our users will enter their credentials to authenticate themselves.
I am working on a developer tool called WiseGPT, a promptless Generative AI for React developers. It's like having a pair of extra hands that write code in your style, without any context limit. It even provides API integration by accepting Postman collection and supports extending UI in the VSCode itself. It's a great tool to speed up your development process and I highly recommend giving it a try.
Now, back to our login page. We'll create a simple form with fields for the username and password. We'll also add a submit button that will trigger the authentication process when clicked.
Here's a code snippet to create a basic login form:
1 import React, { useState } from 'react'; 2 3 export default function Login() { 4 const [username, setUsername] = useState(''); 5 const [password, setPassword] = useState(''); 6 7 const handleSubmit = (event) => { 8 event.preventDefault(); 9 // Handle the login logic here 10 }; 11 12 return ( 13 <form onSubmit={handleSubmit}> 14 <label> 15 Username: 16 <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} /> 17 </label> 18 <label> 19 Password: 20 <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} /> 21 </label> 22 <input type="submit" value="Submit" /> 23 </form> 24 ); 25 } 26
In the above code snippet, we're using React's useState hook to manage the state of the username and password fields. When the form is submitted, the handleSubmit function is called. This is where we'll handle the authentication logic, which we'll cover in the next section.
Now that we've got our login page up and running, it's time to implement the user authentication. This is where we'll check the user's credentials and, if they're valid, allow the user to access the protected routes in our application.
User authentication can be a complex process, involving server-side API calls, handling tokens, and managing the user's authentication status. But thanks to tools like WiseGPT, we can simplify this process. With its promptless Generative AI, it can write the authentication code in your style, handle API integration, and even extend the UI in VSCode itself. It's like having a super-smart pair programming buddy!
Let's start by creating a mock authentication function. In a real-world application, this function would make an API call to the server to verify the user's credentials. But for simplicity, we'll just simulate a delay using setTimeout and resolve the promise if the username and password are both "admin".
Here's a code snippet for the mock authentication function:
Next, we'll use this authenticate function in our handleSubmit function in the Login component. If the authentication is successful, we'll redirect the user to the home page. If it fails, we'll display an error message.
Here's the updated Login component:
1 import React, { useState } from 'react'; 2 import { useNavigate } from 'react-router-dom'; 3 4 export default function Login() { 5 const [username, setUsername] = useState(''); 6 const [password, setPassword] = useState(''); 7 const [error, setError] = useState(''); 8 const navigate = useNavigate(); 9 10 const handleSubmit = async (event) => { 11 event.preventDefault(); 12 try { 13 await authenticate(username, password); // Assuming you have the authenticate function 14 navigate('/'); 15 } catch { 16 setError('Invalid username or password'); 17 } 18 }; 19 20 return ( 21 <form onSubmit={handleSubmit}> 22 <label> 23 Username: 24 <input 25 type="text" 26 value={username} 27 onChange={(e) => setUsername(e.target.value)} 28 /> 29 </label> 30 <label> 31 Password: 32 <input 33 type="password" 34 value={password} 35 onChange={(e) => setPassword(e.target.value)} 36 /> 37 </label> 38 {error && <p>{error}</p>} 39 <input type="submit" value="Submit" /> 40 </form> 41 ); 42 } 43
In the above code snippet, we're using the useNavigate hook from React Router to programmatically navigate to different routes. If the authentication is successful, we navigate to the home page. If it fails, we set the error state, which is then displayed above the submit button.
Now that we've implemented user authentication, let's move on to setting up protected routes. These are routes that can only be accessed by authenticated users. If an unauthenticated user tries to access a protected route, they should be redirected to the login page.
But with WiseGPT, you can generate this boilerplate code with a few clicks. Its Generative AI understands your coding style and generates code accordingly. It can even handle API integration and extend the UI in VSCode. It's like having an extra pair of hands that can write code just the way you want it!
Now, let's start setting up our protected routes. We'll create a ProtectedRoute component that wraps the Route component from React Router. This ProtectedRoute component will check the user's authentication status and either render the component passed as a prop or redirect the user to the login page.
Here's a code snippet for the ProtectedRoute component:
1 import React from 'react'; 2 import { Route, Navigate } from 'react-router-dom'; 3 4 export default function ProtectedRoute({ component: Component, ...rest }) { 5 const isAuthenticated = // Check the user's authentication status here 6 7 return ( 8 <Route {...rest}> 9 {isAuthenticated ? <Component /> : <Navigate to="/login" />} 10 </Route> 11 ); 12 } 13
In the above code snippet, we're using the Navigate component from React Router to redirect unauthenticated users to the login page. The Component prop is the component that should be rendered if the user is authenticated.
We've set up our protected routes, but what happens when an unauthenticated user tries to access these routes? They should be redirected to the login page, right? That's exactly what we're going to implement in this section.
In the previous section, we created a ProtectedRoute component that checks the user's authentication status. If the user is not authenticated, the ProtectedRoute component uses the Navigate component from React Router to redirect the user to the login page.
But there's one more thing we need to handle. What if the user tries to access a protected route directly by entering the URL in the browser? In this case, after the user logs in, they should be redirected back to the protected route they were trying to access, not the home page.
To handle this, we can use the state prop of the Navigate component. We can pass the current location to the state prop, and then use this state in the login page to redirect the user back to the protected route after successful login.
Here's how you can update the ProtectedRoute component to pass the current location to the state prop:
1 import React from 'react'; 2 import { Route, Navigate, useLocation } from 'react-router-dom'; 3 4 export default function ProtectedRoute({ component: Component, ...rest }) { 5 const isAuthenticated = // Check the user's authentication status here 6 const location = useLocation(); 7 8 return ( 9 <Route {...rest}> 10 {isAuthenticated ? ( 11 <Component /> 12 ) : ( 13 <Navigate to="/login" state={{ from: location }} /> 14 )} 15 </Route> 16 ); 17 } 18
And here's how you can update the handleSubmit function in the Login component to redirect the user back to the protected route:
1 const handleSubmit = async (event) => { 2 event.preventDefault(); 3 try { 4 await authenticate(username, password); 5 const from = location.state?.from || '/'; 6 navigate(from); 7 } catch { 8 setError('Invalid username or password'); 9 } 10 }; 11
In the above code snippet, we're using the location state to get the route the user was trying to access. If the location state is not defined (i.e., the user navigated to the login page directly), we default to the home page.
As our React application grows, we might find ourselves in situations where we need to nest routes within other routes. For example, in a blogging app, you might have a route for a blog post (/post/:id), and within that route, you might have another route for the comments (/post/:id/comments). This is where nested routes come into play.
In React Router, you can create nested routes by placing Route components inside other Route components. The child Route will only match if the parent Route's path also matches.
Let's say we want to create a Profile page that has two nested routes - Posts and Comments. Here's how you can do it:
1 import React from 'react'; 2 import { BrowserRouter as Router, Route, Link, Outlet } from 'react-router-dom'; 3 4 export default function App() { 5 return ( 6 <Router> 7 <nav> 8 <Link to="/profile/posts">Posts</Link> 9 <Link to="/profile/comments">Comments</Link> 10 </nav> 11 <Route path="/profile" element={<Profile />}> 12 <Route path="posts" element={<Posts />} /> 13 <Route path="comments" element={<Comments />} /> 14 </Route> 15 </Router> 16 ); 17 } 18 19 function Profile() { 20 return ( 21 <div> 22 <h2>Profile</h2> 23 <Outlet /> 24 </div> 25 ); 26 } 27 28 function Posts() { 29 return <h3>Posts</h3>; 30 } 31 32 function Comments() { 33 return <h3>Comments</h3>; 34 } 35
In the above code snippet, the Profile component is rendered when you navigate to "/profile". Inside the Profile component, we use the Outlet component to render the child routes. When you navigate to "/profile/posts", the Posts component is rendered, and when you navigate to "/profile/comments", the Comments component is rendered.
React Router provides several hooks that we can use in our components to interact with the router. Two of these hooks are useNavigate and useLocation.
The useNavigate hook returns a function that we can use to navigate programmatically to different routes. We've already seen this hook in action in our Login component.
Here's a simple example of how you can use the useNavigate hook:
1 import { useNavigate } from 'react-router-dom'; 2 3 export default function Home() { 4 const navigate = useNavigate(); 5 6 const goToProfile = () => { 7 navigate('/profile'); 8 }; 9 10 return ( 11 <div> 12 <h2>Home</h2> 13 <button onClick={goToProfile}>Go to profile</button> 14 </div> 15 ); 16 } 17
In the above code snippet, when the "Go to profile" button is clicked, the goToProfile function is called, which navigates to the "/profile" route.
The useLocation hook returns an object representing the current location. This object has several properties, including pathname (the current path), search (the query string), and state (the state object passed to navigate).
Here's a simple example of how you can use the useLocation hook:
1 import { useLocation } from 'react-router-dom'; 2 3 export default function CurrentLocation() { 4 const location = useLocation(); 5 6 return ( 7 <div> 8 <h2>Current location</h2> 9 <p>Pathname: {location.pathname}</p> 10 <p>Search: {location.search}</p> 11 <p>State: {JSON.stringify(location.state)}</p> 12 </div> 13 ); 14 } 15
In the above code snippet, we're displaying the current pathname, search, and state.
We've come a long way! We've set up our React project, created a login page, implemented user authentication, set up protected routes, and learned about nested routes and React Router hooks. Now, it's time to test our authentication flow and see if everything works as expected.
To test the authentication flow, follow these steps:
Here's a simple Logout component you can use to log out:
1 import { useNavigate } from 'react-router-dom'; 2 3 export default function Logout() { 4 const navigate = useNavigate(); 5 6 const handleLogout = () => { 7 // Clear the authentication token here 8 navigate('/login'); 9 }; 10 11 return ( 12 <button onClick={handleLogout}>Logout</button> 13 ); 14 } 15
In the above code snippet, when the "Logout" button is clicked, the handleLogout function is called, which clears the authentication token and navigates to the login page.
We're almost at the finish line! We've successfully implemented the login and authentication flow in our React application. Now, let's focus on the logout functionality and how to clean up after a user logs out.
When a user logs out, we need to do two things:
We've already created a simple Logout component in the previous section. Now, let's see how we can clear the authentication token.
Assuming you're storing the authentication token in the local storage, you can clear it using the localStorage.removeItem function:
1 const handleLogout = () => { 2 localStorage.removeItem('authToken'); 3 navigate('/login'); 4 }; 5
In the above code snippet, we're removing the authentication token from the local storage and then navigating to the login page.
If you're storing the authentication token in a state or context, you can clear it by setting the state or context value to null or an empty string.
That's it for handling logout and cleaning up! You've successfully implemented React Router authentication in your React application. Give yourself a pat on the back, you've done a great job!
Congratulations! You've successfully implemented React Router authentication in your React application. You've learned how to set up a new React project, create a login page, implement user authentication, set up protected routes, redirect unauthenticated users, create nested routes, and use React Router's useNavigate and useLocation hooks. That's a lot to take in, and you should be proud of what you've accomplished!
But the learning doesn't stop here. There's always more to learn and explore. Here are some next steps you can take:
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.