Documentation Index Fetch the complete documentation index at: https://mintlify.com/academind/react-complete-guide-course-resources/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Authentication in React applications typically involves managing user tokens, protecting routes, and coordinating authentication state across the application.
This guide covers token-based authentication using localStorage, React Router actions, and route protection strategies.
Create a flexible authentication form supporting both login and signup:
import {
Form ,
Link ,
useSearchParams ,
useActionData ,
useNavigation ,
} from 'react-router-dom' ;
function AuthForm () {
const data = useActionData ();
const navigation = useNavigation ();
const [ searchParams ] = useSearchParams ();
const isLogin = searchParams . get ( 'mode' ) === 'login' ;
const isSubmitting = navigation . state === 'submitting' ;
return (
< Form method = "post" >
< h1 > { isLogin ? 'Log in' : 'Create a new user' } </ h1 >
{ data && data . errors && (
< ul >
{ Object . values ( data . errors ). map (( err ) => (
< li key = { err } > { err } </ li >
)) }
</ ul >
) }
{ data && data . message && < p > { data . message } </ p > }
< p >
< label htmlFor = "email" > Email </ label >
< input id = "email" type = "email" name = "email" required />
</ p >
< p >
< label htmlFor = "password" > Password </ label >
< input id = "password" type = "password" name = "password" required />
</ p >
< div >
< Link to = { `?mode= ${ isLogin ? 'signup' : 'login' } ` } >
{ isLogin ? 'Create new user' : 'Login' }
</ Link >
< button disabled = { isSubmitting } >
{ isSubmitting ? 'Submitting...' : 'Save' }
</ button >
</ div >
</ Form >
);
}
export default AuthForm ;
Use query parameters (?mode=login or ?mode=signup) to toggle between login and signup modes on the same page.
Authentication Action
Handle authentication requests using React Router actions:
import { json , redirect } from 'react-router-dom' ;
export async function action ({ request }) {
const searchParams = new URL ( request . url ). searchParams ;
const mode = searchParams . get ( 'mode' ) || 'login' ;
if ( mode !== 'login' && mode !== 'signup' ) {
throw json ({ message: 'Unsupported mode.' }, { status: 422 });
}
const data = await request . formData ();
const authData = {
email: data . get ( 'email' ),
password: data . get ( 'password' ),
};
const response = await fetch ( 'http://localhost:8080/' + mode , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ( authData ),
});
if ( response . status === 422 || response . status === 401 ) {
return response ;
}
if ( ! response . ok ) {
throw json ({ message: 'Could not authenticate user.' }, { status: 500 });
}
const resData = await response . json ();
const token = resData . token ;
localStorage . setItem ( 'token' , token );
return redirect ( '/' );
}
This example uses localStorage for token storage. For production applications, consider security implications and potentially use httpOnly cookies for sensitive tokens.
Token Management
Create utility functions to manage authentication tokens:
// util/auth.js
import { redirect } from 'react-router-dom' ;
export function getAuthToken () {
const token = localStorage . getItem ( 'token' );
return token ;
}
export function tokenLoader () {
return getAuthToken ();
}
export function checkAuthLoader () {
const token = getAuthToken ();
if ( ! token ) {
return redirect ( '/auth' );
}
return null ;
}
These loader functions can be attached to routes to provide authentication checks and data.
Route Protection
Protect routes by adding authentication loaders:
Protected Routes
Public Routes
import { checkAuthLoader } from './util/auth' ;
const router = createBrowserRouter ([
{
path: '/' ,
element: < RootLayout /> ,
children: [
{
path: 'events/new' ,
element: < NewEventPage /> ,
loader: checkAuthLoader , // Protect this route
action: newEventAction ,
},
{
path: 'events/:eventId/edit' ,
element: < EditEventPage /> ,
loader: checkAuthLoader , // Protect this route
action: editEventAction ,
},
],
},
]);
Conditional UI Rendering
Show/hide UI elements based on authentication state:
import { useRouteLoaderData } from 'react-router-dom' ;
function MainNavigation () {
const token = useRouteLoaderData ( 'root' );
return (
< nav >
< ul >
< li >< Link to = "/" > Home </ Link ></ li >
< li >< Link to = "/events" > Events </ Link ></ li >
{ token && (
<>
< li >< Link to = "/events/new" > New Event </ Link ></ li >
< li >
< Form method = "post" action = "/logout" >
< button > Logout </ button >
</ Form >
</ li >
</>
) }
{ ! token && (
< li >< Link to = "/auth?mode=login" > Login </ Link ></ li >
) }
</ ul >
</ nav >
);
}
useRouteLoaderData allows you to access loader data from parent routes using their route ID.
Logout Implementation
Implement logout functionality:
Logout Action
Route Configuration
Usage in Component
// pages/Logout.js
import { redirect } from 'react-router-dom' ;
export function action () {
localStorage . removeItem ( 'token' );
return redirect ( '/' );
}
Authenticated API Requests
Include authentication tokens in API requests:
import { getAuthToken } from './auth' ;
export async function createEvent ( eventData ) {
const token = getAuthToken ();
const response = await fetch ( 'http://localhost:8080/events' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'Authorization' : 'Bearer ' + token ,
},
body: JSON . stringify ( eventData ),
});
if ( ! response . ok ) {
throw new Error ( 'Failed to create event' );
}
return response . json ();
}
Include the token in the Authorization header as a Bearer token for API authentication.
Token Expiration
Handle token expiration and automatic logout:
export function getTokenDuration () {
const storedExpirationDate = localStorage . getItem ( 'expiration' );
const expirationDate = new Date ( storedExpirationDate );
const now = new Date ();
const duration = expirationDate . getTime () - now . getTime ();
return duration ;
}
export function getAuthToken () {
const token = localStorage . getItem ( 'token' );
if ( ! token ) {
return null ;
}
const tokenDuration = getTokenDuration ();
if ( tokenDuration < 0 ) {
return 'EXPIRED' ;
}
return token ;
}
export function tokenLoader () {
const token = getAuthToken ();
if ( token === 'EXPIRED' ) {
localStorage . removeItem ( 'token' );
localStorage . removeItem ( 'expiration' );
return null ;
}
return token ;
}
Auto-Logout Timer
Implement automatic logout when token expires:
import { useEffect } from 'react' ;
import { useNavigate , useLoaderData , useSubmit } from 'react-router-dom' ;
import { getTokenDuration } from './util/auth' ;
function App () {
const token = useLoaderData ();
const submit = useSubmit ();
useEffect (() => {
if ( ! token ) {
return ;
}
if ( token === 'EXPIRED' ) {
submit ( null , { action: '/logout' , method: 'post' });
return ;
}
const tokenDuration = getTokenDuration ();
const logoutTimer = setTimeout (() => {
submit ( null , { action: '/logout' , method: 'post' });
}, tokenDuration );
return () => {
clearTimeout ( logoutTimer );
};
}, [ token , submit ]);
return < RouterProvider router = { router } /> ;
}
Security Best Practices
Development : localStorage is convenient for development and non-sensitive applications.Production : Consider using httpOnly cookies for storing authentication tokens to prevent XSS attacks:// Server sets httpOnly cookie
res . cookie ( 'token' , token , {
httpOnly: true ,
secure: true ,
sameSite: 'strict' ,
maxAge: 3600000 ,
});
Always validate tokens on the server side: // Server-side validation
const isValidToken = await verifyJWT ( token );
if ( ! isValidToken ) {
return res . status ( 401 ). json ({ message: 'Invalid token' });
}
Handle authentication errors gracefully: if ( response . status === 401 ) {
// Token expired or invalid
localStorage . removeItem ( 'token' );
return redirect ( '/auth?error=session-expired' );
}
Common Patterns
Persistent Login Store token expiration time: const expirationTime = new Date ();
expirationTime . setHours (
expirationTime . getHours () + 1
);
localStorage . setItem (
'expiration' ,
expirationTime . toISOString ()
);
Refresh Tokens Implement token refresh mechanism: async function refreshToken () {
const response = await fetch (
'/api/refresh' ,
{
method: 'POST' ,
credentials: 'include' ,
}
);
return response . json ();
}
Routing Learn about React Router setup and navigation
Deployment Deploy authenticated applications securely