🔄 Global State in React Without Context API or External Libraries

🔄 Global State in React Without Context API or External Libraries
Anmol KansalAnmol KansalApr 5, 20252 min read

Managing the global state is a common challenge in React. While tools like Redux, Zustand, or even React’s built-in Context API are popular choices, they can sometimes feel like overkill for simpler use cases.

What if you could share the state between components without any of that?

Let’s build a custom hook that does just that — global state without context, in under 30 lines of code.

✅ When Would You Use This?

  • You need a shared state across a few components
  • You want to avoid boilerplate
  • You don't need advanced features like middleware or persistence

🧠 useGlobalState Implementation

The idea is to make a simple shared store using closure and use subscription pattern -

// globalState.ts
import { useEffect, useState } from 'react';

type Subscriber<T> = (value: T) => void;

function createGlobalState<T>(initialValue: T) {
    let state = initialValue;
    const subscribers = new Set<Subscriber<T>>();

    const setState = (newValue: T) => {
        state = newValue;
        subscribers.forEach((callback) => callback(state));
    };

    const useGlobalState = () => {
        const [localState, setLocalState] = useState(state);

        useEffect(() => {
            const callback = (newValue: T) => setLocalState(newValue);
            subscribers.add(callback);
            return () => subscribers.delete(callback);
        }, []);

        return [localState, setState] as const;
    };

    return useGlobalState;
}

🧪 Example Usage

We can call createGlobalState fn to get a global hook -

const useGlobalCounter = createGlobalState(0);

const ComponentA = () => {
  const [count, setCount] = useGlobalCounter();
  return <button onClick={() => setCount(count + 1)}>Increment A: {count}</button>;
};

const ComponentB = () => {
  const [count, setCount] = useGlobalCounter();
  return <button onClick={() => setCount(count + 1)}>Increment B: {count}</button>;
};

Use both components anywhere in your tree, and they’ll stay in sync without a context provider.

⚠️ Caveats

  • It won’t work well for deep React trees with frequent updates.
  • Use wisely — for complex apps, dedicated state libraries are still a better fit.

📌 Summary

This pattern offers a minimal, dependency-free way to manage shared states in React apps. Great for small to mid-sized apps or feature-level global states — no Context, no boilerplate.

Give it a try and see how far you can go without reaching for Redux or Context. 🚀

Anmol Kansal

Anmol Kansal

Full Stack Developer

I have close to 5 years of industry experience building scalable web interfaces and robust frontend infrastructures. I specialize in React, TypeScript, Next.js, and full-stack JavaScript — with a strong focus on clean code, performant bundle loading, and highly optimized animation layers.

More blogs