Mastering User Authentication with React Router v6
Setting Up React Router v6
To get started, create a new React project and install React Router v6 using npm or yarn. Then, edit the src/main.js
file to import BrowserRouter
from react-router-dom
and wrap the <App />
component with <BrowserRouter />
.
import { BrowserRouter } from 'eact-router-dom';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
Basic Routing with React Router v6
React Router v6 provides two primary components for routing: <Routes />
and <Route />
. The <Routes />
component is an alternative to the <Switch />
component from React Router v5, while the <Route />
component provides the mapping between paths on the app and different React components.
import { Routes, Route } from 'eact-router-dom';
import HomePage from './pages/HomePage';
import LoginPage from './pages/LoginPage';
function App() {
return (
<Routes>
<Route path="/" element=<HomePage /> />
<Route path="/login" element=<LoginPage /> />
</Routes>
);
}
Creating Protected Routes
Protected routes are essential for restricting access to certain pages or resources to authenticated users only. To implement protected routes, create a custom useAuth
Hook to manage the authenticated user’s state using React’s Context API and useContext
Hook.
import { createContext, useContext, useState } from 'eact';
const AuthContext = createContext();
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = (userData) => {
setUser(userData);
};
const logout = () => {
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
Implementing Two-Factor Authentication
Two-factor authentication adds an extra layer of security by requiring users to provide two distinct forms of identification before accessing sensitive features. To implement two-factor authentication, modify the existing authentication setup to include 2FA.
import { useAuth } from './useAuth';
function LoginPage() {
const { login } = useAuth();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [twoFactorCode, setTwoFactorCode] = useState('');
const handleSubmit = async (event) => {
event.preventDefault();
try {
await login(username, password, twoFactorCode);
} catch (error) {
console.error(error);
}
};
return (
<form onSubmit={handleSubmit}>
<label>
Username:
<input type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
</label>
<br />
<label>
Password:
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
</label>
<br />
<label>
2FA Code:
<input type="text" value={twoFactorCode} onChange={(e) => setTwoFactorCode(e.target.value)} />
</label>
<br />
<button type="submit">Login</button>
</form>
);
}
Using Nested Routes and <Outlet />
Nested routes allow us to have a route that contains other child routes. The <Outlet />
component enables nested UI elements to be visible when child routes are rendered.
import { Routes, Route, Outlet } from 'eact-router-dom';
import HomePage from './pages/HomePage';
import LoginPage from './pages/LoginPage';
import ProfilePage from './pages/ProfilePage';
function App() {
return (
<Routes>
<Route path="/" element=<HomePage /> />
<Route path="/login" element=<LoginPage /> />
<Route path="/profile" element=<>
<Outlet />
<Route index element=<ProfilePage /> />
</Route>
</Routes>
);
}
function ProfilePage() {
return (
<div>
<h1>Profile Page</h1>
<p>This is the profile page.</p>
</div>
);
}