.Net application development specialists
asp.net, c#, vb.net, html, javascript, jquery, html, xhtml, css, oop, design patterns, sql server, mvc and much more
contact: admin@paxium.co.uk

Paxium is the company owned by myself, Dave Amour and used for providing IT contract development services including


  • Application development - Desktop, Web, Services - with Classic ASP, Asp.net WebForms, Asp.net MVC, Asp.net Core
  • Html, Css, JavaScript, jQuery, React, C#, SQL Server, Ado.net, Entity Framework, NHibernate, TDD, WebApi, GIT, IIS
  • Database schema design, implementation & ETL activities
  • Website design and hosting including email hosting
  • Training - typically one to one sessions
  • Reverse Engineering and documentation of undocumented systems
  • Code Reviews
  • Performance Tuning
  • Located in Cannock, Staffordshire
Rugeley Chess Club Buying Butler Cuckooland Katmaid Pet Sitting Services Roland Garros 60 60 Golf cement Technical Conformity Goofy MaggieBears Vacc Track Find Your Smart Phone eBate Taylors Poultry Services Lafarge Rebates System Codemasters Grid Game eBate DOFF

Understanding React Contexts (Light/Dark Mode Example)

React Contexts let you share data across deeply nested components without having to pass props down multiple levels. They are ideal for global or page-wide state such as themes, authentication, or language settings.

1) Imports

import { createContext, useContext } from "react";

We typically work with two main pieces when using context:

  • The Context – defines the “channel” through which data is shared.
  • The Provider – wraps components and supplies the data to that channel.

This avoids “prop drilling”, where props have to be manually passed down through many layers of components.

Provider Context Consumer

2) The Context Example: ThemeContext.jsx

import { createContext, useContext } from "react";

export const ThemeContext = createContext(null);

export function useTheme() {
  const ctx = useContext(ThemeContext);
  if (!ctx) throw new Error("useTheme must be used within <ThemeProvider>");
  return ctx; // { theme, toggleTheme }
}

The createContext(null) call sets up the context channel. We initialise it with null so that if a component tries to use it outside of a provider, the custom useTheme() hook will throw an error.

Some developers instead provide a default object for autocomplete support: createContext({ theme: "light", toggleTheme: () => {} }), but this example enforces correct usage strictly through the provider.

3) The Provider Example: ThemeProvider.jsx

import { useEffect, useMemo, useRef, useState } from "react";
import { ThemeContext } from "./ThemeContext";

const STORAGE_KEY = "app_theme"; // "light" | "dark"

function getInitialTheme() {
  const saved = localStorage.getItem(STORAGE_KEY);
  if (saved === "light" || saved === "dark") return saved;
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}

export default function ThemeProvider({ children }) {
  const [theme, setTheme] = useState(getInitialTheme);
  const rootRef = useRef(null);

  useEffect(() => {
    localStorage.setItem(STORAGE_KEY, theme);
    if (rootRef.current) rootRef.current.setAttribute("data-theme", theme);
  }, [theme]);

  const value = useMemo(
    () => ({
      theme,
      toggleTheme: () => setTheme(t => (t === "light" ? "dark" : "light")),
    }),
    [theme]
  );

  return (
    <ThemeContext.Provider value={value}>
      <div ref={rootRef} className="context-demo-theme" data-theme={theme}>
        {children}
      </div>
    </ThemeContext.Provider>
  );
}
  • getInitialTheme() checks localStorage and the user’s system preference.
  • useRef stores a reference to the wrapper <div> so we can update its data-theme attribute for CSS styling.
  • useMemo ensures the value object only changes when theme changes, avoiding unnecessary re-renders.

This provider keeps the theme scoped to this page only. A global app theme provider would normally attach data-theme to <html> or <body> instead.

4) Using the Provider: ContextDemo.jsx

import ThemeProvider from "../theme/ThemeProvider";
import ThemedButton from "../components/ThemedButton";
import "./ContextDemo.css";

export default function ContextDemo() {
  return (
    <ThemeProvider>
      <main className="app">
        <h1>Context Demo (page-scoped theme)</h1>
        <ThemedButton />
        <p>Only this page changes theme. Navigate elsewhere and it won’t be dark.</p>
      </main>
    </ThemeProvider>
  );
}

Here, ThemeProvider wraps the page content, allowing any child (like ThemedButton) to access and modify the theme.

5) Consuming the Context: ThemedButton.jsx

import { useTheme } from "../theme/ThemeContext";

export default function ThemedButton() {
  const { theme, toggleTheme } = useTheme();

  return (
    <button onClick={toggleTheme}>
      Current theme: {theme} (click to toggle)
    </button>
  );
}
  • The useTheme() hook exposes the context value returned by the provider.
  • theme is either "light" or "dark".
  • toggleTheme flips the mode and persists it to localStorage.

6) Styling: ContextDemo.css

.context-demo-theme[data-theme="light"] {
  --bg: white;
  --fg: black;
}

.context-demo-theme[data-theme="dark"] {
  --bg: #222;
  --fg: white;
}

.context-demo-theme {
  background-color: var(--bg);
  color: var(--fg);
  min-height: 100vh;
  transition: all 0.3s ease;
}

The data-theme attribute enables scoped theming through CSS variables. When the user toggles the theme, only elements inside this provider update their appearance.

✅ Summary

  • createContext defines the shared data channel.
  • Provider supplies data and functions to that channel.
  • useContext (wrapped in a custom hook) allows child components to consume it easily.
  • useMemo and useEffect manage performance and persistence.
  • Ref + data attributes provide CSS hooks for styling.

This example shows how Context can elegantly manage cross-component state such as dark/light mode — locally or globally — with minimal prop passing.