Technical Deepdive: Performant React Context

Projects

August 2, 2025

What is React Context?

React Context is a way to pass data through the component tree without having to pass props down manually at every level.

  • Intuitive: Context totally makes sense in the context of modern React, it's a hook just like useState or useEffect

  • Easy to implement: Very little boilerplate, just a few lines of code

  • Scalable: It's a powerful tool, you reach any level of complexity with it adding reducers and other hooks.

But there is one major issue with Context: Performance

Context is a powerful tool, but it can also be a performance bottleneck. When a component re-renders, all of its children will re-render, even if the data hasn't changed. Worse, if you have nested Context providers, the performance will degrade exponentially because, after all, they are children too.

We could argue that in a perfectly architectured application, this is not a problem. But perfection is rarely achieved and if it is it fades away eventually. In a real world application, we will have to deal with this issue.

Achieving performant Context

There are a few ways to achieve performant Context.

  • Use a memoized context: Use a memoized context to avoid re-rendering the context provider when the data hasn't changed.

  • Implement selective subscriptions: Only subscribe to the specific state slices you need

  • Use useSyncExternalStore: Leverage React's built-in external store hook for better control

  • Separate read and write operations: Split state access from state updates

  • Implement manual subscription management: Control exactly when components re-render

Building a Fast Context Implementation

Let's build a performant context implementation that addresses these issues. The key is to bypass React's built-in context re-rendering mechanism and implement our own subscription system.

Here's how we can create a fast context that only re-renders components when their specific data changes:

The goal is to make components subscribe directly to state changes rather than relying on context propagation.

Core Architecture

Our fast context implementation uses several key techniques:

  • Reference-based state: Use useRef instead of useState to prevent Provider re-renders

  • Manual subscription system: Implement custom listeners with Set for O(1) operations

  • useSyncExternalStore: Leverage React's external store hook for better control

  • Selective subscriptions: Components only subscribe to their specific data needs

  • Immutable updates: Ensure referential equality for unchanged state portions

Implementation Details

The implementation consists of three main parts:

  • Store: A lightweight state container with get, set, and subscribe methods

  • Provider: A component that creates and provides the store without re-rendering

  • Hooks: useSelector for reading state and useSet for updating state

The store uses useRef to maintain state, preventing the Provider from re-rendering when state changes. Instead, it manually notifies subscribers through a custom subscription system.

Performance Benefits

This approach provides several significant performance improvements:

  • Zero Provider re-renders: The Provider component never re-renders, regardless of state changes

  • Selective component updates: Only components using changed data re-render

  • Predictable performance: Performance doesn't degrade with component tree depth

  • Efficient memory usage: Automatic cleanup prevents memory leaks

  • Scalable architecture: Performance remains consistent as the application grows

Real-World Comparison

In our demo, you can see the difference between classic React Context and our fast implementation.

  • Classic Context: All components re-render when any state changes, even if they don't use that data

  • Fast Context: Only components using changed data re-render, others remain untouched

This becomes especially important in large applications where you might have hundreds of components in the tree.

When to Use Fast Context

Fast context is ideal for:

  • Large component trees: When you have many components that need access to shared state

  • Frequent state updates: Applications with high-frequency state changes

  • Performance-critical applications: Where every re-render matters

  • Complex state management: When you need fine-grained control over re-renders

For simple applications with infrequent updates, classic React Context might be sufficient. But as your application grows, the performance benefits become increasingly valuable.

Best Practices

When implementing fast context, follow these best practices:

  • Use specific selectors: Only select the data you actually need

  • Keep selectors pure: Avoid creating new objects or arrays in selectors

  • Implement proper cleanup: Always return cleanup functions from subscriptions

  • Test performance: Measure re-render counts to verify optimizations

  • Document patterns: Help your team understand when and how to use the fast context


Conclusion

React Context is a powerful tool, but it comes with performance trade-offs. By implementing a fast context pattern, we can maintain the simplicity and developer experience of Context while achieving the performance characteristics of external state management libraries.

The key insight is that we don't need to rely on React's built-in context re-rendering mechanism. By implementing our own subscription system and using useSyncExternalStore, we can create a context that only re-renders components when their specific data changes.

This approach scales well and provides predictable performance characteristics, making it suitable for large, performance-critical applications. The implementation is relatively simple but provides significant performance benefits.

Performance optimization is not about premature optimization - it's about building the right architecture from the start.

© 2026 A-Roy