DEV Community

pinniola
pinniola

Posted on

We Fixed React's Context API: Introducing react-signal-context

If you're a React developer, you probably love its simplicity and power. But there's a good chance you've also fought against a silent enemy hiding in plain sight: the unnecessary re-render. And the most common culprit? Ironically, it's a tool we use every day to make our lives easier: the Context API.

For years, the community has accepted an uncomfortable trade-off: use Context to avoid "prop drilling," but then spend hours manually optimizing with React.memo, useMemo, and useCallback to hold back the tide of re-renders that follows.

What if we told you that this trade-off is no longer necessary?

The Problem: Death by a Thousand Re-Renders

The internal workings of React's Context API are like a radio broadcast: when the value in the Provider changes, it transmits the new state to all listening components, indiscriminately.

Imagine a global state with user information, the app's theme, and a list of notifications. If only the theme changes from "light" to "dark," the component displaying the user's name and the one showing notifications are also forced to recalculate everything, even though the data they care about hasn't changed one bit. On a large scale, this leads to a slow but relentless performance degradation.

The Solution: Stop Shouting, Start Subscribing

Inspired by the surgical precision of signals and the simplicity of libraries like Zustand, we created react-signal-context.

It's a drop-in library with an API that's almost identical to the one you already know and love, but with a completely different engine under the hood. Instead of broadcasting every update to everyone, it allows each component to "whisper" to the store exactly which piece of state it's actually interested in.

The result? A component re-renders if and only if the specific data it selected has changed. Nothing more, nothing less.

Seeing is Believing

Let's take a look at how familiar it is.

1. Create your store:

// store.ts
import { createSignalContext } from 'react-signal-context';

export const { Provider, useContext } = createSignalContext((set) => ({
  count: 0,
  theme: 'light',
  increment: () => set((state) => ({ count: state.count + 1 })),
  toggleTheme: () =>
    set((state) => ({
      theme: state.theme === 'light' ? 'dark' : 'light',
    })),
}));
Enter fullscreen mode Exit fullscreen mode

2. Consume state... selectively:

// Counter.tsx
import { useContext } from './store';
import React from 'react';

// This component will ONLY re-render if 'count' or 'increment' changes.
// It will completely ignore theme updates.
const Counter = React.memo(() => {
  const count = useContext((state) => state.count);
  const increment = useContext((state) => state.increment);

  console.log('Counter component is rendering!');

  return <button onClick={increment}>Count: {count}</button>;
});
Enter fullscreen mode Exit fullscreen mode
// ThemeDisplay.tsx
import { useContext } from './store';
import React from 'react';

// This component will ONLY re-render if 'theme' changes.
const ThemeDisplay = React.memo(() => {
  const theme = useContext((state) => state.theme);

  console.log('Theme component is rendering!');

  return <p>Current theme: {theme}</p>;
});
Enter fullscreen mode Exit fullscreen mode

With this approach, performance becomes the default, not a manual optimization.

Our Promise: The Performance of Zustand with the Simplicity of the Context API

react-signal-context is designed to be a "pit of success." We want to make it easy to do the right thing.

Zero Refactoring: Switching from the standard React.useContext just requires adding a selector.

Lightweight: No external dependencies, just pure React.

Modern & Future-Proof: It leverages the useSyncExternalStore hook, the official React-recommended approach for integrating with external stores, ensuring full compatibility with concurrent features.

Try it Now!

We are incredibly excited to share this tool with the community. We believe it can genuinely improve the lives of many React developers.

📦 Install via npm:

npm install react-signal-context
Enter fullscreen mode Exit fullscreen mode

🔗 Explore the code on GitHub: [Link to your GitHub repository]

📖 Read the docs: [Link to your README.md on GitHub or a documentation site]

Try it in your next project, or introduce it into an existing one to solve a performance bottleneck. Let us know what you think by opening an issue or a discussion on GitHub.


It's time to stop fighting re-renders and start building faster React applications, the easy way.

What are your experiences with React Context performance issues? Have you found other solutions? Let's discuss in the comments! 👇

Top comments (0)