Context API — Global State
What is Context API?
The Context API solves prop drilling — the problem of passing props through many layers of components that don't need them. Context lets you share data globally across the component tree without passing props at every level.
When to use Context: Theme, language, authenticated user, shopping cart — data that many components need. For complex state logic, consider Zustand or Redux instead.
// contexts/AuthContext.jsx
import { createContext, useContext, useState } from 'react'
const AuthContext = createContext(null)
// Provider component — wraps the app
export function AuthProvider({ children }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(false)
const login = async (email, password) => {
setLoading(true)
try {
// Simulate API call
const userData = await fakeLogin(email, password)
setUser(userData)
} finally {
setLoading(false)
}
}
const logout = () => setUser(null)
const value = { user, loading, login, logout, isLoggedIn: !!user }
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
)
}
// Custom hook — cleaner API + error checking
export function useAuth() {
const context = useContext(AuthContext)
if (!context) throw new Error('useAuth must be used within AuthProvider')
return context
}
// Usage in any component:
// const { user, login, logout, isLoggedIn } = useAuth()
// contexts/ThemeContext.jsx
import { createContext, useContext, useState, useEffect } from 'react'
const ThemeContext = createContext(null)
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState(() => {
// Initialize from localStorage
return localStorage.getItem('theme') || 'light'
})
useEffect(() => {
localStorage.setItem('theme', theme)
document.documentElement.setAttribute('data-theme', theme)
}, [theme])
const toggleTheme = () => setTheme(t => t === 'light' ? 'dark' : 'light')
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
)
}
export function useTheme() {
const context = useContext(ThemeContext)
if (!context) throw new Error('useTheme must be used within ThemeProvider')
return context
}
// ThemeToggle component — uses context
export function ThemeToggle() {
const { theme, toggleTheme } = useTheme()
return (
<button onClick={toggleTheme}>
{theme === 'light' ? '🌙 Dark' : 'â˜€ï¸ Light'}
</button>
)
}
// App.jsx — compose providers
import { AuthProvider } from './contexts/AuthContext'
import { ThemeProvider, ThemeToggle } from './contexts/ThemeContext'
import { useAuth } from './contexts/AuthContext'
function Navbar() {
const { user, logout, isLoggedIn } = useAuth()
return (
<nav>
<span>{isLoggedIn ? `Hello, ${user.name}` : 'Guest'}</span>
<ThemeToggle />
{isLoggedIn && <button onClick={logout}>Logout</button>}
</nav>
)
}
function LoginPage() {
const { login, loading } = useAuth()
return (
<button onClick={() => login('alice@example.com', 'password')} disabled={loading}>
{loading ? 'Logging in...' : 'Login'}
</button>
)
}
function App() {
return (
// Nest providers — order matters (inner can access outer)
<ThemeProvider>
<AuthProvider>
<Navbar />
<LoginPage />
</AuthProvider>
</ThemeProvider>
)
}
Ready to Level Up Your Skills?
Explore 500+ free tutorials across 20+ languages and frameworks.