Create a PrivateRoute component that consumes your auth context. In version 6 custom route components have fallen out of favor, the preferred method is to use an auth layout component. How to create a protected route with react-router-dom and storing the response in local storage, so that when a user tries to open next time they can view their details again. After login, they should redirect to the dashboard page. Your login component also doesn't handle redirecting back to any "home" page or private routes that were originally being accessed.
import { Suspense, useState } from "react";
import { Routes, Route, Navigate } from "react-router-dom";
interface props {
path: string;
MatchComponent: string;
}
const ProtectedRoutes = ({ path, MatchComponent }: props) => {
const [isLoggedIn] = useState(true);
return (
<>
{isLoggedIn ? (
<>
<Routes>
<Route
path={path}
element={
<Suspense fallback={<h1>Loading...</h1>}>
<MatchComponent />
</Suspense>
}
/>
</Routes>
</>
) : (
<Routes>
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
)}
</>
);
};
export default ProtectedRoutes;
<BrowserRouter>
<Switch>
{authLogin ? (
<>
<Route path="/dashboard" component={Dashboard} exact />
<Route exact path="/About" component={About} />
</>
) : (
<Route path="/" component={Login} exact />
)}
<Route component={PageNotFound} />
</Switch>
</BrowserRouter>
const PrivateRoute = (props) => {
const location = useLocation();
const { authLogin } = useContext(globalC);
if (authLogin === undefined) {
return null; // or loading indicator/spinner/etc
}
return authLogin ? (
<Route {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>
);
};
Render all your routes in a "flat list"
function Routes() {
return (
<BrowserRouter>
<Switch>
<PrivateRoute path="/dashboard" component={Dashboard} />
<PrivateRoute path="/About" component={About} />
<Route path="/login" component={Login} />
<Route component={PageNotFound} />
</Switch>
</BrowserRouter>
);
}
import { Navigate, Outlet } from 'react-router-dom';
const PrivateRoutes = () => {
const location = useLocation();
const { authLogin } = useContext(globalC);
if (authLogin === undefined) {
return null; // or loading indicator/spinner/etc
}
return authLogin
? <Outlet />
: <Navigate to="/login" replace state={{ from: location }} />;
}
Protected Routes are routes that can only be accessed if a condition is met(usually, if user is properly authenticated). It returns a Route that either renders a component or redirects a user to another route based on a set condition. Can we just wrap all the restricted routes under a protected route component? and just render them as { children } if a condition is met? No. Usually the reason protected routes are protected is because these routes communicate with the backend server in a way that requires authentication, and simply changing "isAuthenticated" wont make the backend server trust us, since our credentials that we pass in the requests are the same.
const handleInputChange = (e) => {
setUserData((prevState) => {
return {
...prevState,
[e.target.name]: e.target.value,
};
});
};
const handleSubmit = (e) => {
e.preventDefault();
//if username or password field is empty, return error message
if (userData.username === "" || userData.password === "") {
setErrorMessage((prevState) => ({
value: "Empty username/password field",
}));
} else if (
userData.username.toLowerCase() === "admin" &&
userData.password === "123456"
) {
//Signin Success
localStorage.setItem("isAuthenticated", "true");
window.location.pathname = "/";
} else {
//If credentials entered is invalid
setErrorMessage((prevState) => ({
value: "Invalid username/password"
}));
return;
}
};
import React from "react";
import { Redirect, Route } from "react-router-dom";
function ProtectedRoute({ component: Component, ...restOfProps }) {
const isAuthenticated = localStorage.getItem("isAuthenticated");
console.log("this", isAuthenticated);
return (
<Route
{...restOfProps}
render={(props) =>
isAuthenticated ? <Component {...props} /> : <Redirect to="/signin" />
}
/>
);
}
export default ProtectedRoute;
This is a React Router tutorial which teaches you how to use Private Routes with React Router 6. The code for this React Router v6 tutorial can be found over here. A React Router tutorial which teaches you how to use Nested Routes with React Router 6 . The code for this React Router v6 tutorial can be found over here . In order to get you started, create a new… A better way of protecting both sibling routes with the same authorization level would be using a Layout Route wich renders the ProtectedRoute component for both nested routes:
We will start off with a minimal React project that uses React Router to navigate a user from one page to another page. In the following function component, we have matching Link and Route components from React Router for various routes. Furthermore, we have a so-called Index Route loaded with the Landing component and a so-called No Match Route loaded with inline JSX. Both act as fallback routes:
import { Routes, Route, Link } from 'react-router-dom';
const App = () => { return ( <> <h1>React Router</h1>
<Navigation />
<Routes> <Route index element={<Landing />} /> <Route path="landing" element={<Landing />} /> <Route path="home" element={<Home />} /> <Route path="dashboard" element={<Dashboard />} /> <Route path="analytics" element={<Analytics />} /> <Route path="admin" element={<Admin />} /> <Route path="*" element={<p>There's nothing here: 404!</p>} /> </Routes> </> );};
const Navigation = () => ( <nav> <Link to="/landing">Landing</Link> <Link to="/home">Home</Link> <Link to="/dashboard">Dashboard</Link> <Link to="/analytics">Analytics</Link> <Link to="/admin">Admin</Link> </nav>);
In the following, we want to protect all routes (except for the Landing route, because it's a public route) from unauthorized access. Each page has a different authorization mechanism. Only the Home and Dashboard pages share the same authorization requirements:
const Landing = () => { return <h2>Landing (Public: anyone can access this page)</h2>;};
const Home = () => { return <h2>Home (Protected: authenticated user required)</h2>;};
const Dashboard = () => { return <h2>Dashboard (Protected: authenticated user required)</h2>;};
const Analytics = () => { return ( <h2> Analytics (Protected: authenticated user with permission 'analyze' required) </h2> );};
const Admin = () => { return ( <h2> Admin (Protected: authenticated user with role 'admin' required) </h2> );};
We will start off by simulating a user login/logout mechanism. By using two buttons conditionally rendered, we either render a login or logout button based on the authentication status of the user. Based on the event handler, we either set a user or reset it to null by using React's useState Hook:
const App = () => { const [user, setUser] = React.useState(null);
const handleLogin = () => setUser({ id: '1', name: 'robin' }); const handleLogout = () => setUser(null);
return ( <> <h1>React Router</h1>
<Navigation />
{user ? ( <button onClick={handleLogout}>Sign Out</button> ) : ( <button onClick={handleLogin}>Sign In</button> )}
<Routes> <Route index element={<Landing />} /> <Route path="landing" element={<Landing />} /> <Route path="home" element={<Home user={user} />} /> ... </Routes> </> );};
We protected our first React component with React Router. However, this approach does not scale, because we would have to implement the same logic in every protected route. In addition, the redirect logic should not reside in the Home component itself but as a best practice protect it from the outside instead. Therefore, we will extract the logic into a standalone component:
const ProtectedRoute = ({ user, children }) => { if (!user) { return <Navigate to="/landing" replace />; }
return children;};
Then we can use this new protecting route component as wrapper for the Home component. The Home component itself does not need to know about this guarding mechanism anymore:
const App = () => { ...
return ( <> ...
<Routes> <Route index element={<Landing />} /> <Route path="landing" element={<Landing />} /> <Route path="home" element={ <ProtectedRoute user={user}> <Home /> </ProtectedRoute> } /> ... </Routes> </> );};
const Home = () => { return <h2>Home (Protected: authenticated user required)</h2>;};
Often times when building a web app, you'll need to protect certain routes in your application from users who don't have the proper authentication. Protected routes let us choose which routes users can visit based on whether they are logged in. For example, you might have public routes that you want anyone accessing, like a landing page, a pricing page, and the login page. Protected routes should only be available to users that are logged in, like a dashboard or settings page. This is just one example of how you can use React Router to add protected routes to your React application. Because React Router embraces React's composition model, you can compose it together in any way that makes sense for your app. I know, another newsletter pitch - but hear me out. Most JavaScript newsletters are terrible. When’s the last time you actually looked forward to getting one? Even worse, when’s the last time you actually read one? We wanted to change that.
import * as React from "react";
const authContext = React.createContext();
function useAuth() { const [authed, setAuthed] = React.useState(false);
return { authed, login() { return new Promise((res) => { setAuthed(true); res(); }); }, logout() { return new Promise((res) => { setAuthed(false); res(); }); }, };}
export function AuthProvider({ children }) { const auth = useAuth();
return <authContext.Provider value={auth}>{children}</authContext.Provider>;}
export default function AuthConsumer() { return React.useContext(authContext);}
import * as React from "react";import { Link, Routes, Route } from "react-router-dom";
const Home = () => <h1>Home (Public)</h1>;const Pricing = () => <h1>Pricing (Public)</h1>;
const Dashboard = () => <h1>Dashboard (Private)</h1>;const Settings = () => <h1>Settings (Private)</h1>;
const Login = () => <h1>TODO</h1>;
function Nav() { return ( <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/pricing">Pricing</Link> </li> </ul> </nav> );}
export default function App() { return ( <div> <Nav />
<Routes> <Route path="/" element={<Home />} /> <Route path="/pricing" element={<Pricing />} /> <Route path="/dashboard" element={<Dashboard />} /> <Route path="/settings" element={<Settings />} /> <Route path="/login" element={<Login />} /> </Routes> </div> );}
import { useNavigate } from "react-router-dom";import useAuth from "./useAuth";
const Login = () => { const navigate = useNavigate(); const { login } = useAuth();
const handleLogin = () => { login().then(() => { navigate("/dashboard"); }); };
return ( <div> <h1>Login</h1> <button onClick={handleLogin}>Log in</button> </div> );};
<Routes> <Route path="/" element={<Home />} /> <Route path="/pricing" element={<Pricing />} /> <Route path="/dashboard" element={ <RequireAuth> <Dashboard /> </RequireAuth> } /> <Route path="/settings" element={ <RequireAuth> <Settings /> </RequireAuth> } /> <Route path="/login" element={<Login />} /></Routes>
function RequireAuth({ children }) { const { authed } = useAuth();
return authed === true ? children : <Navigate to="/login" replace />;}
Protected routes are routes that require user authorization in order to be accessed. When you are building a web application, some of your routes may require authentication, which means restricting user access to certain pages or you having your whole application behind a login. React router is a great way to go when it comes to routing, but you don’t really have the option to protect routes from being accessed by anyone. Luckily the solution to this is really simple and straightforward. To summarise: Even though how your protected routes work in React will be obfuscated, the way your client application works can be reverse-engineered. This is why it is essential that everything that involves authentication or authorization should be backed up by a server-side implementation.
{
"name": "auth",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
+"react-router-dom": "^5.1.2",
"react-scripts": "3.3.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
import React from 'react';
import ReactDOM from 'react-dom';
+ import { Route, BrowserRouter, Switch } from 'react-router-dom';
import './index.css';
+ import Login from './Login';
+ import Dashboard from './Dashboard';
+ import Settings from './Settings';
+ import ProtectedRoute from './ProtectedRoute';
import * as serviceWorker from './serviceWorker';
+ ReactDOM.render((
+ <BrowserRouter>
+ <Switch>
+ <Route path="/login" component={Login} />
+ <ProtectedRoute exact={true} path="/" component={Dashboard} />
+ <ProtectedRoute path="/settings" component={Settings} />
+ <ProtectedRoute component={Dashboard} />
+ </Switch>
+ </BrowserRouter>
+ ), document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
import React from 'react'
import { Redirect } from 'react-router-dom'
class ProtectedRoute extends React.Component {
render() {
const Component = this.props.component;
const isAuthenticated = ???;
return isAuthenticated ? (
<Component />
) : (
<Redirect to={{ pathname: '/login' }} />
);
}
}
export default ProtectedRoute;
In the context of React web application, protected routes are those routes that only grant access permission to authorized users. For example, while making a login request for your social media account you need to provide an id and password to access a specific route. Next, we need to protect routes to the dashboard, so only the authenticated users can access it. In this article, we will learn how to create protected routes in the React web application all from scratch.
// create a React application
npx create - react - app protect - routes - react
cd protect - routes - react npm start
// Empty React application
function App() {
return;
}
export default App;
import * as React from "react";
const authContext = React.createContext();
function useAuth() {
const [authed, setAuthed] = React.useState(false);
return {
authed,
login() {
return new Promise((res) => {
setAuthed(true);
res();
});
},
logout() {
return new Promise((res) => {
setAuthed(false);
res();
});
},
};
}
export function AuthProvider({
children
}) {
const auth = useAuth();
return {
children
};
}
export default function AuthConsumer() {
return React.useContext(authContext);
}
import * as React from "react";
import {
Link,
Routes,
Route
} from "react-router-dom";
const Home = () => Home(Public);
const About = () => About(Public);
const Dashboard = () => Dashboard(Private);
const Login = () => TODO;
function Nav() {
return (
Home
About
);
}
export default function App() {
return (
}
/>
}
/>
}
/>
}
/>
);
}