
ReactJS
Context values don't have to be static. By combining Context with `useState`, you can create dynamic values that update throughout your app.
The key is to put your state and its setter function into the Context value. Any component can then access and update that state:
import { createContext, useContext, useState } from "react";
const ThemeContext = createContext({
isDark: false,
toggleTheme: () => {},
});
function App() {
const [isDark, setIsDark] = useState(false);
const toggleTheme = () => setIsDark(!isDark);
const value = { isDark, toggleTheme };
return (
<ThemeContext.Provider value={value}>
<Header />
<Content />
</ThemeContext.Provider>
);
}
function Header() {
const { isDark, toggleTheme } = useContext(ThemeContext);
return (
<header style={{ background: isDark ? "#1a1a1a" : "#fff" }}>
<button onClick={toggleTheme}>
Switch to {isDark ? "light" : "dark"} mode
</button>
</header>
);
}When you click the button, `toggleTheme()` updates the state, and all components using this Context automatically re-render with the new value.
Since the state is in the Provider, multiple components can trigger updates:
import { createContext, useContext, useState } from "react";
const CounterContext = createContext({
count: 0,
increment: () => {},
decrement: () => {},
});
function App() {
const [count, setCount] = useState(0);
const value = {
count,
increment: () => setCount(count + 1),
decrement: () => setCount(count - 1),
};
return (
<CounterContext.Provider value={value}>
<Display />
<Controls />
</CounterContext.Provider>
);
}
function Display() {
const { count } = useContext(CounterContext);
return <h1>Count: {count}</h1>;
}
function Controls() {
const { increment, decrement } = useContext(CounterContext);
return (
<div>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}Display shows the count. When Controls updates it, Display automatically updates.
<details>
<summary>📚 More Examples</summary>
// Example 1: Modal visibility context
const ModalContext = createContext({
isOpen: false,
openModal: () => {},
closeModal: () => {},
});
function App() {
const [isOpen, setIsOpen] = useState(false);
const value = {
isOpen,
openModal: () => setIsOpen(true),
closeModal: () => setIsOpen(false),
};
return (
<ModalContext.Provider value={value}>
<Main />
</ModalContext.Provider>
);
}
function OpenButton() {
const { openModal } = useContext(ModalContext);
return <button onClick={openModal}>Open Modal</button>;
}
function Modal() {
const { isOpen, closeModal } = useContext(ModalContext);
if (!isOpen) return null;
return (
<div>
<h2>Modal Content</h2>
<button onClick={closeModal}>Close</button>
</div>
);
}
// Example 2: User authentication context
const AuthContext = createContext({
user: null as User | null,
login: async (email: string, password: string) => {},
logout: () => {},
});
function App() {
const [user, setUser] = useState<User | null>(null);
const login = async (email: string, password: string) => {
// Call API...
setUser({ id: "1", email, name: "Alice" });
};
const logout = () => {
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout }}>
<App />
</AuthContext.Provider>
);
}</details>
A common pattern is persisting theme preference to localStorage:
import { createContext, useContext, useState, useEffect } from "react";
type Theme = "light" | "dark";
const ThemeContext = createContext({
theme: "light" as Theme,
setTheme: (theme: Theme) => {},
});
function ThemeProvider({ children }) {
const [theme, setTheme] = useState<Theme>(() => {
// Load from localStorage on startup
const saved = localStorage.getItem("theme");
return (saved as Theme) || "light";
});
// Save to localStorage whenever theme changes
useEffect(() => {
localStorage.setItem("theme", theme);
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);
const value = { theme, setTheme };
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
function ThemeToggle() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Current: {theme}
</button>
);
}
function App() {
return (
<ThemeProvider>
<ThemeToggle />
<Content />
</ThemeProvider>
);
}| Pattern | Use Case | Example |
|---|---|---|
| Single useState | One piece of state | Theme, language |
| Multiple states | Related data | User, theme, modal |
| With callbacks | State + actions | Count with increment |
| With localStorage | Persistence | Saved preferences |
Ready to practice? Challenges | Next: Context Performance
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward