I’m a software developer turned business strategist. Back in 2014, I built my first website using jQuery and Bootstrap—scrappy, functional, and proudly self-taught. But over the years, family, business growth, and shifting priorities pulled me away from the dev scene. Web development became distant… until recently.
With the surge of AI tooling and the vibe coding renaissance, everything changed. Platforms like GitHub Copilot, Cursor, Claude Code, Windsurf, Bolt.new, and Replit reignited my curiosity. React and Next.js stood out as a gateway into modern frontend architecture.
While React was new to me, frontend logic wasn’t. I had previously built and published over 25 Android apps using Flutter. That knowledge was dusty—but tools like Replit and Copilot made getting back into the game feel smoother than ever. What followed was a journey through hooks, components, and an intriguing little thing called the Context API.
🌩️ The Initial Confusion: Theme Switching Gone Wrong
When I started learning React again, I marveled at how elegant yet confusing it could be. One particular feature gave me sleepless nights: the Context API, especially when using it to manage themes across a React app.
As a product builder or an AI developer, sharing state across your application without prop-drilling is essential. And understanding useContext()
is non-negotiable.
Here’s my journey from confusion to clarity, and the practical solution that finally made things click.
Problem Statement: Prop Drilling Gets Ugly
Like many returning developers, I started by building a simple React app—an about-me page, a couple of headings, and a few paragraphs styled based on the user’s preferred theme. The logic was easy to grasp: I defined a theme
state at the top level (light
or dark
) and tried to pass it down through components using props.
It worked… for a while. But then I added a header. And a page section. And a nested text component. Suddenly, every component was asking for theme
, even ones that didn’t directly care about it.
Here’s what that looked like:
<Page theme={theme} />
<Header theme={theme} />
Then inside Page
, and then inside Title:
<Title theme={theme} />
<Paragraph theme={theme} />
const Title = ({ theme, children }) => {
return (
<h1 style={{ color: theme === "dark" ? "white" : "black" }}>
{children}
</h1>
);
};
Prop drilling felt like plumbing a leaky house. One upstream change required downstream rewiring.
That’s when I turned to React’s Context API—a way to share global state across components without prop chaos.

While React felt like new territory, I wasn’t a stranger to frontend frameworks. I’d spent years working with Flutter, and had shipped over 25 Android apps. That skill had started gathering dust… until I discovered a new wave of game-changing tools like Replit, Windsurf, Cursor, Claude Code, GitHub Copilot, Bolt.new, and so many others that reignited my love for building. Suddenly, coding wasn’t just efficient—it was exhilarating.
🧱 Building the Theme Context (Step-by-Step)
If you’re new to React Context, think of it as a broadcast system. You define a “channel” (ThemeContext
), provide it with values (like theme
), and let any component “subscribe” to it without manual prop passing.
1️⃣ Create the Context
import { createContext } from "react";
export const ThemeContext = createContext();
This sets up the channel. No data yet—just the frequency.
2️⃣ Set Up the ThemeProvider
import React, { useState } from "react";
import { ThemeContext } from "./ThemeContext";
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme(prev => (prev === "dark" ? "light" : "dark"));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
React requires hooks to be inside components—don’t define useState
globally.
3️⃣ Create a Custom Hook for Easy Consumption
import { useContext } from "react";
import { ThemeContext } from "./ThemeContext";
export const useTheme = () => useContext(ThemeContext);
Then in any component:
const { theme, toggleTheme } = useTheme();
This keeps your UI logic clean and centralized.
🧵 Why My Theme Toggle Was Buggy (Closure & Batching in Action)
One of my early bugs looked like this:
function toggleTheme() {
setTheme(theme === "dark" ? "light" : "dark");
}
Seems logical, right?
React disagreed. I’d see inconsistent toggles. Sometimes the theme would double-flip or not switch at all.
🔍 The Problem: Closure Scope + Batching
In JavaScript, closures “remember” variables from when they were created—even if they’ve changed since. My function was working with an outdated copy of theme
, especially when React batched multiple state updates into one render cycle.
The fix?
setTheme(prev => (prev === "dark" ? "light" : "dark"));
This guarantees the latest value—making toggling bulletproof.
🧪 Classic Batched State Bug Example
Here’s a bug I’d seen in counters too:
setCount(count + 1);
setCount(count + 1);
You expect the count to be 2
, but it stays 1
. Why? Both lines use stale count
.
✅ The fix:
setCount(c => c + 1);
setCount(c => c + 1);
Each updater receives the freshest value—even when batched.
🎨 Building a Themed Component
Once my context was solid, building reactive UI was seamless.
Example: A title that adjusts color based on the theme:
const Title = ({ children }) => {
const { theme } = useTheme();
return (
<h1 style={{ color: theme === "light" ? "black" : "white" }}>
{children}
</h1>
);
};
And a layout wrapper:
const PageWrapper = ({ children }) => {
const { theme } = useTheme();
return (
<div style={{ backgroundColor: theme === "light" ? "#fff" : "#111" }}>
{children}
</div>
);
};
No props. No confusion. Just clean, context-driven styling.
❌ Mistakes I Made (and You Can Dodge)
Here’s a quick table of the roadblocks I hit and how I fixed them:
Mistake | Why It Fails | Fix It With |
---|---|---|
Defining useState() outside a component | React throws errors because hooks need a valid scope | Move hooks into your ThemeProvider component |
Passing props unnecessarily | Leads to bloated hierarchies and duplication | Use context + custom hook |
Using stale theme in toggle | Closures capture outdated state during batching | Use setTheme(prev => ...) |
Overstyling components manually | Hard to maintain across layouts | Use conditional styles from context |
Writing useContext("dark") | Context expects an object, not a static string | Always use your ThemeContext reference |

💼 Real-World Use Cases
Use Case | Where It Fits | Why It Works |
---|---|---|
SaaS Dashboards | Dark mode switcher for admin panels | Consistent theming without manual config |
AI Dev Tools | Overlay, system, or performance views | Global mode switching for UX |
Startup Sites | Personalization settings | Improves branding and accessibility |
Reader-Focused Blogs | Night mode for reduced eye strain | User-centric design choices |
✅ Quick Reference Cheatsheet
Do This | Don’t Do This |
---|---|
useContext(ThemeContext) | useContext("dark") |
setTheme(prev => ...) | setTheme(theme === "dark") |
Wrap app in ThemeProvider | Use context without a wrapper |
Use useTheme() hook | Drill props across components |
🔚 Final Thoughts: From Friction to Flow
This theme toggle journey reminded me of what I love about dev work: solving meaningful problems with thoughtful architecture. React’s Context API unlocked a new layer of clarity in my app logic. Hooks became less mysterious, closures more predictable, and the UI… beautifully reactive.
Whether you’re crafting SaaS dashboards, AI-powered visualizers, or blog UIs, mastering context will save you hours and scale your app gracefully. I fumbled through it—so you don’t have to.
Want a starter repo, demo link, or companion Notion template to build your own theme toggler? I’ve got plenty more ideas where this came from ⚛️🛠️