
Modular Redux Design Pattern for Scalable, Shared State in React
The day of massive, single-page-applications is upon us. Websites are looking more and more like applications and less like collections of pages. The median JavaScript size for the most popular 20 websites of 2019 is 3.5 megabytes. All but two had at least 1 megabyte of JavaScript.
Single page apps require a new way of thinking about web design. Instead of storing client session state on the server, more and more is stored in the client. State is one of the trickiest things to manage in a growing code-base. It's particularly tricky when the state is shared across multiple components in your client-side application. Thankfully there are tools that can help. Shared-state libraries help with understanding and debugging the state across multiple components.
In this article I'm going to focus on Redux, one of the most popular solutions to managing shared client side state. Redux really shines when it comes to understanding and debugging shared state:
- Redux's serializable, immutable, shared state is easy to inspect, persist, and restore
- Redux's serializable, atomic, dispatched updates are easy to inspect, record and playback
The Problem with Redux
Redux is the de facto standard for React shared-state, and there's good reason. With centralized serializable state, serializable actions, atomic updates in the form of reducers, not to mention great middleware, there's a lot to recommend Redux. State predictability and debuggability is the name of the game, and Redux is a best-of-class solution to shared state management… almost.
Redux is a pain to use and doesn't scale. Even on the smallest projects it takes an excessive amount of 'boilerplate' code to get up and running. On large projects, it only gets worse. Redux's complexity scales poorly with project size. Except… it's not actually Redux that's the problem. It's the way it is used. Both the traditional Redux and newer redux-toolkit design patterns are inherently complex and don't scale.
Redux is a best-of-class solution to shared state management
Thankfully, Redux can be used in a way that is both easy and scales. We can have our cake and eat it too. It's possible to have all the wonderful properties of Redux and do it with svelte, clean, scalable code. We just need a change of design pattern.
The 5 Essential Elements of Modular Software Design
This article is a follow-up to my previous article on the elements of modular design. Where the previous article applied to software engineering in general, this article is all about the nitty-gritty of real-world React-Redux applications. My goal here is to show how to write better, more scalable Redux code, and also to use Redux as a real-world case-study of the benefits and techniques of modular design.
What is Modular Redux?
Modular Redux is the result of consistently applying good, modular design to applications using Redux. Modular Redux is a design pattern. It's a re-usable solution to using Redux in real world applications. From the point of view of modular design, there were four main opportunities for improving Redux design patterns:
- Encapsulate Redux modules with respect to each other: Reducers typically have access to all redux state and all dispatched actions. We can modularize Redux slices so each slice only has access to its own sub-state and actions.
- Consolidate all code related to one Redux slice into one module: All reducers, action-types, subscriptions, getters and dispatchers can be consolidated for each Redux slice.
- Isolate Redux from components: Components should not be directly dependent on Redux. We should implement the simplest possible API for each Redux-module.
- Minimize component shared-state dependencies: If a component doesn't directly use or update shared state, it shouldn't be dependent on it.
If a component doesn't directly use or update shared state, it shouldn't be dependent on it.
Comparing Modular Redux and Traditional Redux
Here's why modular Redux really matters: it can reduce the complexity of shared state by two to three times. Modular redux slashes the dependencies between modules. The traditional implementation has 19 dependencies between modules, while the Modular-Redux solution has only 7 dependencies.
There are zero indirect dependencies with Modular Redux. Each module can be fully swapped out without needing to update other modules. The relationships are clear and easy to understand. Minimized dependencies maximize code scalability.
Modular Redux vs Traditional Redux:
- 2.7x improvement: 7 dependencies vs 19
- 1.8x improvement: 5 files vs 9
- 1.6x improvement: 104 lines of code vs 170
Comparing Modular Redux and Redux Toolkit
The Redux Toolkit website provides a basic tutorial, an intermediate tutorial and an advanced tutorial. In detailed comparisons using these tutorials, Modular Redux consistently outperforms Redux Toolkit:
Intermediate Tutorial—Modular Redux vs Redux Toolkit:
- 2.1x improvement: 13 dependencies vs 27
- 2.0x improvement: 193 lines of code vs 393
- 30% improvement: 10 files vs 13
- 30% improvement: 134k minimized JavaScript build vs 170k
Advanced Tutorial—Modular Redux vs Redux Toolkit:
- 2.4x improvement: 20 redux-related dependencies, vs 48
- 1.8x improvement: 35 overall dependencies, vs 63
- 1.7x improvement: 685 lines of code, vs 1169
- 10% improvement: 244k minimized JavaScript build, vs 271k
Streamlining Modular Redux with Hooks-for-Redux (H4R)
If you decide to use the Modular Redux pattern, I recommend using hooks-for-redux. It is a tiny library (currently just 90 lines of code). It implements the common parts of the Modular Redux design pattern in a well-tested and fully TypeScripted way.
Modular Redux Scales - It's also Just Easier
Redux is a very simple idea. It lets you define shared state, methods for atomic updates and subscriptions to changes. Where Redux really shines is the wealth of middleware available you can attach to a redux store to persist, restore, playback and inspect your state. Because of all those reasons, Redux should scale very well, but it typically doesn't. The problem isn't Redux itself. The problem is the recommended ways of using Redux introduces a ton unnecessary complexity.
With Modular Design, Redux becomes a Best-of-Class solution to React shared state
The Modular Redux design pattern dramatically simplifies using Redux. While making things easier is great, Modular Redux shines most as you scale your application. Modular Redux kept the complexity under control for each of the applications presented even as they increased in size. This comes from the systematic application of the essential elements of modular software design across all aspects of Redux.
Get Started
To get started, I recommend taking one of the examples or diving into the Hooks-for-Redux Tutorial which is more detailed and will guide you towards the best way to use Modular Redux in your application.

Shane Brinkman-Davis Delamore
Shane Brinkman-Davis Delamore is a master of the craft and passionate about UX design and programmer productivity.
How can we help?
Can we help you apply these ideas on your project? Send us a message! You'll get to talk with our awesome delivery team on your very first call.
Get in Touch