What is Context API in React? With an Authentication Example

Surajit DasSurajit Das
3 min read

Introduction

As React apps grow, managing and sharing state between components becomes complex. That's where the Context API comes in — a built-in feature in React that provides a simpler and cleaner alternative to prop drilling or heavy state management libraries like Redux.

What is Context API?

Context API allows you to create global variables or state that can be passed around the component tree, without the need to manually pass props through every level.

Core Components of Context API

  • React.createContext() - Creates a new Context object.

  • Provider (<MyContext.Provider>) -Wraps the part of your app where you want the context to be available.

  • Consumer or useContext(MyContext) - Allows access to the context data inside any child component.

When to Use It

  • Sharing authentication state.

  • Managing themes, language, or user preferences.

  • Handling cart or UI state globally.

Project Example: Authentication System Using Context API

We’ll build a system where:

  • The user logs in and gets a token.

  • The token is stored in Context API and localStorage.

  • The token is accessible app-wide without prop drilling.

  • No API calls are made from the context itself (only in components).

Folder Structure

src/
├── components/
│   └── Login.jsx
├── context/
│   └── AuthContext.jsx
├── pages/
│   └── Home.jsx
│   └── Dashboard.jsx
├── routes/
│   └── router.jsx
└── App.jsx

Step-by-Step Implementation

Create Auth Context — src/context/AuthContext.jsx

import { createContext, useContext, useState } from 'react';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [token, setToken] = useState(() => localStorage.getItem('token'));

  const login = (newToken) => {
    setToken(newToken);
    localStorage.setItem('token', newToken);
  };

  const logout = () => {
    setToken(null);
    localStorage.removeItem('token');
  };

  return (
    <AuthContext.Provider value={{ token, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

Notes:

  • We initialize the token from localStorage to persist it across reloads.

  • login() and logout() update both state and localStorage.

Define Routes — src/routes/router.jsx

import { createBrowserRouter } from 'react-router-dom';
import Home from '../pages/Home';
import Dashboard from '../pages/Dashboard';
import Login from '../components/Login';

const router = createBrowserRouter([
  { path: '/', element: <Home /> },
  { path: '/dashboard', element: <Dashboard /> },
  { path: '/login', element: <Login /> }
]);

export default router;

App Entry Point — src/App.jsx

import { RouterProvider } from 'react-router-dom';
import router from './routes/router';
import { AuthProvider } from './context/AuthContext';

function App() {
  return (
    <AuthProvider>
      <RouterProvider router={router} />
    </AuthProvider>
  );
}

export default App;

Login Component — src/components/Login.jsx

import { useState } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';

const Login = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { login } = useAuth();
  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const res = await axios.post('https://your-api.com/api/users/login', {
        email,
        password
      });
      login(res.data.token);
      navigate('/dashboard');
    } catch (err) {
      console.error('Login failed:', err);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <h2>Login</h2>
      <input
        type="email"
        placeholder="Email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <input
        type="password"
        placeholder="Password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button type="submit">Login</button>
    </form>
  );
};

export default Login;

Home Page — src/pages/Home.jsx

const Home = () => {
  return <h1>Welcome to Home Page</h1>;
};

export default Home;

Dashboard — src/pages/Dashboard.jsx

import { useAuth } from '../context/AuthContext';

const Dashboard = () => {
  const { token, logout } = useAuth();

  return (
    <div>
      <h1>Dashboard</h1>
      {token ? (
        <>
          <p>Your token: {token}</p>
          <button onClick={logout}>Logout</button>
        </>
      ) : (
        <p>You are not logged in.</p>
      )}
    </div>
  );
};

export default Dashboard;

Final Thoughts

  • Context API is great for sharing global data like auth state across your app.

  • You should not use Context for all state — only for truly global needs (like auth).

  • Using localStorage in combination with Context ensures persistence across refreshes.

0
Subscribe to my newsletter

Read articles from Surajit Das directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Surajit Das
Surajit Das