DEV Community

Cover image for Introducing react-state-custom: A Hook-First State Management Library
Vo Thanh Dat
Vo Thanh Dat

Posted on

Introducing react-state-custom: A Hook-First State Management Library

React 19 is on the horizon, and many of us are looking for ways to simplify state management without sacrificing performance or type safety. If you’ve been frustrated by heavy boilerplate or global stores that trigger unnecessary re-renders, react‑state‑custom may be the lightweight alternative you’ve been waiting for.

Why another state library?

Many popular solutions rely on reducers and dispatch functions, introducing patterns that feel disconnected from the rest of your React code. React‑state‑custom takes a different approach: it’s hook‑first and event‑driven. You write your state logic using the same hooks you already know (useState, useEffect, useMemo, etc.) and share it across components with a single line of code.

Key benefits include:

  • Zero boilerplate: no reducers or actions.
  • Selective re‑renders: components only update when the data they actually use changes.
  • Automatic context management: create and destroy contexts on demand with AutoRootCtx.
  • Built‑in dev tools: inspect all contexts and their current values with a togglable overlay.
  • ~10 KB gzipped: minimal footprint and no external dependencies beyond React.

Getting started

Install the package:

npm install react-state-custom
Enter fullscreen mode Exit fullscreen mode

Example: A simple counter

Here’s what it looks like to set up a counter with shared state:

import {
  createRootCtx,
  createAutoCtx,
  useQuickSubscribe,
  AutoRootCtx
} from "react-state-custom";
import React, { useState } from "react";

// 1. Define your state using regular React hooks
function useCounterState() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(c => c + 1);
  const decrement = () => setCount(c => c - 1);
  return { count, increment, decrement };
}

// 2. Create a root context and hook for consuming it
const { useCtxState } = createAutoCtx(createRootCtx("counter", useCounterState));

// 3. Use the context anywhere in your app
function Counter() {
  const ctx = useCtxState();
  const { count, increment, decrement } = useQuickSubscribe(ctx);
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  );
}

// 4. Mount AutoRootCtx once near the root of your app
function App() {
  return (
    <>
      <AutoRootCtx />
      <Counter />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Notice that there are no reducers or actions; the state is defined with normal hooks and shared via a single createRootCtx call.

Advanced features

  • Granular subscriptions: Subscribe to a single key or multiple keys with useDataSubscribe and useDataSubscribeMultiple. Add debounce or transform data on the fly (useDataSubscribeWithTransform).

  • Proxy-based subscriptions: With useQuickSubscribe, you can destructure context values as if they were local variables; only the accessed fields cause re-renders.

  • Parameterised contexts: Need separate state instances per user or ID? Pass props into useCtxState({ id }), and contexts are automatically keyed by those parameters.

  • Control mount/unmount churn: createAutoCtx(root, delay)accepts an optional unmountDelayMs to keep instances alive briefly after they’re no longer in use. This helps with rapid mount/unmount cycles.

  • Wrapper and debugging props: AutoRootCtx accepts a Wrapper prop (e.g., an error boundary) to handle state errors gracefully and a debugging flag to render raw state snapshots next to your app.

What’s new in v1.0.27?

The most recent release (October 27 2025) introduces:

  • A complete Vitest test suite (60 tests) covering contexts, root and auto contexts, quick subscriptions, and a new useArrayChangeId hook.
  • Node 20+ support and Yarn PnP compatibility.
  • Improved error handling when accessing context data outside the render phase.
  • Optional delay when cleaning up automatic contexts to smooth out rapid mount/unmount cycles.
  • Documentation for the built‑in DevToolContainer component.

If you haven’t tried it yet, now is a great time to explore!

Ready to try it?

Feedback and contributions are welcome—join the conversation on GitHub and help shape the future of hook‑driven state management in React.

Top comments (0)