Redux is usually a singleton, meaning one giant store for the whole app. But what if you want fully isolated Redux stores — one per component instance? Maybe you're rendering independent widgets, dynamic tabs, or embedding apps inside apps. Here's how to create **scoped Redux stores** per component, without polluting the global app state.

Why Scoped Redux Stores?

Common use cases:

  • Multiple instances of the same feature (e.g., chat rooms, dashboards)
  • Micro-frontend components embedded independently
  • Dynamic UIs where each "widget" needs clean, isolated state

Step 1: Create a Store Factory

Instead of a singleton store, make a factory that creates a store per use:

// src/store/createScopedStore.js
import { configureStore } from '@reduxjs/toolkit';
import { reducer } from './slices/scopedSlice';

export function createScopedStore() {
  return configureStore({
    reducer,
  });
}

Step 2: Create a Local Provider

You can't use the global because it only works once. So, create a component that injects its own store dynamically:

// src/components/ScopedStoreProvider.js
import { Provider } from 'react-redux';
import { createScopedStore } from '../store/createScopedStore';

export function ScopedStoreProvider({ children }) {
  const store = createScopedStore();
  return {children};
}

Step 3: Build a Local Slice

This slice will live only inside the scoped store:

// src/store/slices/scopedSlice.js
import { createSlice } from '@reduxjs/toolkit';

const scopedSlice = createSlice({
  name: 'scoped',
  initialState: { count: 0 },
  reducers: {
    increment(state) {
      state.count++;
    },
    decrement(state) {
      state.count--;
    },
  },
});

export const { increment, decrement } = scopedSlice.actions;
export const reducer = scopedSlice.reducer;

Step 4: Use It in a Component

Each instance will have completely independent Redux state!

// src/components/CounterWidget.js
import { ScopedStoreProvider } from './ScopedStoreProvider';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from '../store/slices/scopedSlice';

export function CounterWidget() {
  return (
    
      
    
  );
}

function InnerCounter() {
  const count = useSelector((state) => state.count);
  const dispatch = useDispatch();

  return (
    

Scoped Counter: {count}

); }

How It Works

  • Every ScopedStoreProvider creates a brand new Redux store.
  • The store is private to its subtree — no shared or conflicting state.
  • Perfect for highly modular apps where components must stay isolated.

Pros and Cons

✅ Pros

  • Absolute isolation between instances
  • No risk of state collisions
  • Useful for embedding Redux components into third-party UIs

⚠️ Cons

  • More memory usage (each store is a full Redux store)
  • DevTools need extra configuration to track multiple stores
  • Harder to share cross-instance state without lifting up

🚀 Alternatives

  • Zustand, Recoil: Designed for local/global blending naturally
  • Context + useReducer: Lightweight and scoped, but loses Redux tooling

Summary

If you need true component-level isolation with Redux tooling, scoped stores are your hidden weapon. Use it carefully — it’s a powerful pattern but not always needed in traditional apps.

For a much more extensive guide on getting the most out of React portals, check out my full 24-page PDF file on Gumroad. It's available for just $10:

Using React Portals Like a Pro.

If you found this useful, you can support me here: buymeacoffee.com/hexshift