javascript 137 lines · 4 tabs

Protecting React Routes with an Auth Context and a RequireAuth Wrapper

Shared by codesnips Jul 2026
4 tabs
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { fetchCurrentUser, postLogin, postLogout } from './api';

const AuthContext = createContext(null);

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let active = true;
    fetchCurrentUser()
      .then((u) => { if (active) setUser(u); })
      .catch(() => { if (active) setUser(null); })
      .finally(() => { if (active) setLoading(false); });
    return () => { active = false; };
  }, []);

  const value = useMemo(() => ({
    user,
    loading,
    async login(credentials) {
      const u = await postLogin(credentials);
      setUser(u);
      return u;
    },
    async logout() {
      await postLogout();
      setUser(null);
    },
  }), [user, loading]);

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth() {
  const ctx = useContext(AuthContext);
  if (ctx === null) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return ctx;
}
4 files · javascript Explain with highlit

This snippet shows the standard React way to gate private pages behind authentication without scattering session checks through every component. The design has three parts: a context that owns auth state, a route wrapper that enforces it, and the routing table that composes them.

In AuthContext.jsx, an AuthProvider holds the current user and a loading flag so the app can distinguish "not logged in" from "we don't know yet". On mount it calls fetchCurrentUser() to rehydrate the session from an existing cookie, which matters on a hard refresh where in-memory state is gone. The login and logout functions update state after talking to the API, and the whole value is memoized with useMemo so consumers don't re-render on unrelated parent updates. The useAuth hook wraps useContext and throws when used outside the provider, turning a subtle null-access bug into a loud, immediate error.

The loading flag is the key edge case. Without it, a protected route would briefly see user === null during the async fetch and redirect a genuinely logged-in user to the login page — an annoying flash. RequireAuth.jsx handles this by returning a fallback while loading is true, and only then deciding. When there is no user it renders react-router's Navigate with replace so the login page doesn't pollute the history stack, and it stashes the attempted URL in location.state. That captured from is what lets the login flow send the user back to where they were headed.

When a user is present, RequireAuth renders <Outlet />, which means it works as a layout route wrapping many children rather than guarding one element at a time. App.jsx demonstrates this: public routes like /login sit outside the guard, while everything under the RequireAuth element is protected in one place.

This pattern centralizes the redirect logic, keeps individual pages ignorant of auth, and composes cleanly with nested routes. The trade-off is that it trusts client state for UX only — the server must still authorize every request, since anyone can bypass a client-side guard.


Related snips

ruby
payload = {
  sub: user.id,
  iss: 'https://auth.example.com',
  aud: 'codesnips-api',
  exp: 15.minutes.from_now.to_i,
  iat: Time.now.to_i,

JWT issuance and verification without common footguns

jwt authentication api
by Kai Nakamura 2 tabs
typescript
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'

const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
  timeout: 15000,

Axios API client with interceptors

react axios api
by Maya Patel 1 tab
typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface FilterState {
  search: string
  category: string | null

Zustand for lightweight state management

react zustand state-management
by Maya Patel 2 tabs
typescript
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";

const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";

JWT access + refresh token rotation (conceptual)

security node jwt
by codesnips 3 tabs
typescript
import React from "react";

type FallbackProps = {
  error: Error;
  reset: () => void;
};

React Error Boundary + error reporting hook

react frontend error-boundary
by codesnips 3 tabs
ruby
raw_token = SecureRandom.urlsafe_base64(32)
token_digest = Digest::SHA256.hexdigest(raw_token)

PasswordReset.create!(
  user: user,
  token_digest: token_digest,

Secure random token generation for sessions and recovery flows

randomness tokens authentication
by Kai Nakamura 1 tab

Share this code

Here's the card — post it anywhere.

Protecting React Routes with an Auth Context and a RequireAuth Wrapper — share card
Link copied