Context API
Learn React Context API for sharing state across components without prop drilling
70 min•By Priygop Team•Last updated: Feb 2026
What is Context API?
Context API is a React feature that allows you to share state across components without having to explicitly pass props through every level of the component tree.
When to Use Context
- Sharing global state across many components
- Avoiding prop drilling
- Theme or language preferences
- User authentication state
- Application-wide settings
Basic Context Setup
Example
import React, { createContext, useContext, useState } from 'react';
// Create a context
const ThemeContext = createContext();
// Provider component
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// Custom hook to use the context
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
// Using the context in components
function App() {
return (
<ThemeProvider>
<Header />
<Main />
</ThemeProvider>
);
}
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header className={theme}>
<h1>My App</h1>
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'Dark' : 'Light'}
</button>
</header>
);
}
// Advanced Context with multiple values
const UserContext = createContext();
function UserProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const login = async (credentials) => {
setLoading(true);
try {
// Simulate API call
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
const userData = await response.json();
setUser(userData);
} catch (error) {
throw error;
} finally {
setLoading(false);
}
};
const logout = () => {
setUser(null);
};
return (
<UserContext.Provider value={{ user, loading, login, logout }}>
{children}
</UserContext.Provider>
);
}
function useUser() {
const context = useContext(UserContext);
if (!context) {
throw new Error('useUser must be used within a UserProvider');
}
return context;
}
// Using multiple contexts
function App() {
return (
<ThemeProvider>
<UserProvider>
<Header />
<Main />
</UserProvider>
</ThemeProvider>
);
}
function Header() {
const { theme, toggleTheme } = useTheme();
const { user, logout } = useUser();
return (
<header className={theme}>
<h1>My App</h1>
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'Dark' : 'Light'}
</button>
{user && (
<div>
<span>Welcome, {user.name}</span>
<button onClick={logout}>Logout</button>
</div>
)}
</header>
);
}Practice Exercise: Context API E-commerce
Example
// Exercise: Build an E-commerce App with Context API
// Create a shopping cart system using Context API
// src/contexts/CartContext.js
import React, { createContext, useContext, useReducer } from 'react';
const CartContext = createContext();
const cartReducer = (state, action) => {
switch (action.type) {
case 'ADD_ITEM':
const existingItem = state.items.find(item => item.id === action.payload.id);
if (existingItem) {
return {
...state,
items: state.items.map(item =>
item.id === action.payload.id
? { ...item, quantity: item.quantity + 1 }
: item
)
};
} else {
return {
...state,
items: [...state.items, { ...action.payload, quantity: 1 }]
};
}
case 'REMOVE_ITEM':
return {
...state,
items: state.items.filter(item => item.id !== action.payload)
};
case 'UPDATE_QUANTITY':
return {
...state,
items: state.items.map(item =>
item.id === action.payload.id
? { ...item, quantity: action.payload.quantity }
: item
)
};
case 'CLEAR_CART':
return {
...state,
items: []
};
default:
return state;
}
};
function CartProvider({ children }) {
const [state, dispatch] = useReducer(cartReducer, {
items: [],
total: 0
});
const addItem = (item) => {
dispatch({ type: 'ADD_ITEM', payload: item });
};
const removeItem = (itemId) => {
dispatch({ type: 'REMOVE_ITEM', payload: itemId });
};
const updateQuantity = (itemId, quantity) => {
dispatch({ type: 'UPDATE_QUANTITY', payload: { id: itemId, quantity } });
};
const clearCart = () => {
dispatch({ type: 'CLEAR_CART' });
};
const getTotal = () => {
return state.items.reduce((total, item) => total + (item.price * item.quantity), 0);
};
const getItemCount = () => {
return state.items.reduce((count, item) => count + item.quantity, 0);
};
return (
<CartContext.Provider value={{
items: state.items,
addItem,
removeItem,
updateQuantity,
clearCart,
getTotal,
getItemCount
}}>
{children}
</CartContext.Provider>
);
}
function useCart() {
const context = useContext(CartContext);
if (!context) {
throw new Error('useCart must be used within a CartProvider');
}
return context;
}
// src/components/ProductCard.js
function ProductCard({ product }) {
const { addItem } = useCart();
return (
<div className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>{product.price}</p>
<button onClick={() => addItem(product)}>
Add to Cart
</button>
</div>
);
}
// src/components/Cart.js
function Cart() {
const { items, removeItem, updateQuantity, getTotal, clearCart } = useCart();
if (items.length === 0) {
return <div>Your cart is empty</div>;
}
return (
<div className="cart">
<h2>Shopping Cart</h2>
{items.map(item => (
<div key={item.id} className="cart-item">
<img src={item.image} alt={item.name} />
<div>
<h3>{item.name}</h3>
<p>{item.price}</p>
<input
type="number"
value={item.quantity}
onChange={(e) => updateQuantity(item.id, parseInt(e.target.value))}
min="1"
/>
<button onClick={() => removeItem(item.id)}>
Remove
</button>
</div>
</div>
))}
<div className="cart-total">
<strong>Total: {getTotal()}</strong>
</div>
<button onClick={clearCart}>Clear Cart</button>
</div>
);
}
// src/components/CartIcon.js
function CartIcon() {
const { getItemCount } = useCart();
const itemCount = getItemCount();
return (
<div className="cart-icon">
🛒
{itemCount > 0 && (
<span className="cart-count">{itemCount}</span>
)}
</div>
);
}
// src/App.js
function App() {
return (
<CartProvider>
<div className="app">
<header>
<h1>E-commerce Store</h1>
<CartIcon />
</header>
<main>
<ProductList />
<Cart />
</main>
</div>
</CartProvider>
);
}
// Challenge: Add a wishlist feature
// Challenge: Add product categories and filtering
// Challenge: Add user authentication contextTry It Yourself — Context API
Try It Yourself — Context APIHTML
HTML Editor
✓ ValidTab = 2 spaces
HTML|30 lines|1048 chars|✓ Valid syntax
UTF-8