redux-tab-connect
v1.0.1
Published
A lightweight Redux middleware to sync state across browser tabs using BroadcastChannel API
Maintainers
Readme
redux-tab-connect
🔄 A lightweight Redux middleware to sync state across browser tabs using BroadcastChannel API
Seamlessly synchronize your Redux state across multiple browser tabs in real-time. Perfect for creating consistent user experiences in multi-tab applications.
✨ Features
- 🚀 Real-time sync across multiple browser tabs
- 🎯 Selective syncing with whitelist support
- 📡 Multiple sync strategies for different use cases
- 🛡️ TypeScript support with full type definitions
- 🔧 Zero config - works out of the box
- ⚡ Lightweight - minimal performance impact
- 🔄 Auto state recovery for new tabs
- 🛠️ Error handling with graceful degradation
📦 Installation
Using npm
npm install redux-tab-connectUsing yarn
yarn add redux-tab-connectUsing pnpm
pnpm add redux-tab-connect✅ Package Manager Support: This package is fully compatible with all major package managers (npm, yarn, pnpm). It uses standard CommonJS and ES modules without any package-manager-specific dependencies.
🚀 Quick Start (3 Steps)
1. Setup Redux Store
// store.js
import { configureStore } from '@reduxjs/toolkit';
import { createSyncStateMiddleware, initSyncStateListener, syncStateReducer } from 'redux-tab-connect';
import counterReducer from './counterSlice';
// Wrap your reducer
const wrappedReducer = syncStateReducer(counterReducer);
// Create sync middleware
const middleware = createSyncStateMiddleware({
channelName: 'my-app-sync',
whitelist: ['counter/setIncrement', 'counter/setDecrement', 'counter/setReset']
});
// Configure store
const store = configureStore({
reducer: {
counter: wrappedReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(middleware),
});
// Initialize sync listener
initSyncStateListener(store, 'my-app-sync');
export default store;2. Create Your Slice
// counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: {
count: 0,
},
reducers: {
setIncrement: (state) => {
state.count += 1;
},
setDecrement: (state) => {
state.count -= 1;
},
setReset: (state) => {
state.count = 0;
},
}
});
export const { setIncrement, setDecrement, setReset } = counterSlice.actions;
export default counterSlice.reducer;3. Use in React Component
// App.js
import { Provider, useSelector, useDispatch } from 'react-redux';
import store from './redux/store';
import { setIncrement, setDecrement, setReset } from './redux/counterSlice';
const App = () => {
const { count } = useSelector(state => state.counter);
const dispatch = useDispatch();
return (
<div style={{ padding: '20px' }}>
<h2>Count: {count}</h2>
<button onClick={() => dispatch(setIncrement())}>
Increment (+1)
</button>
<button onClick={() => dispatch(setDecrement())}>
Decrement (-1)
</button>
<button onClick={() => dispatch(setReset())}>
Reset to 0
</button>
<p>✨ Open this app in multiple tabs to see real-time sync!</p>
</div>
);
};
export default () => (
<Provider store={store}>
<App />
</Provider>
);That's it! 🎉 Open your app in multiple tabs and watch the state sync automatically!
⚙️ Sync Strategies
Strategy 1: Basic Counter Sync (Recommended for Testing)
const basicCounterStrategy = {
channelName: 'basic-counter',
whitelist: ['counter/setIncrement', 'counter/setDecrement']
};Strategy 2: Sync Everything (Good for Development)
const syncEverythingStrategy = {
channelName: 'sync-all'
// No whitelist = sync all actions
};Strategy 3: No Sync (Local Only)
const noSyncStrategy = {
channelName: 'no-sync',
whitelist: [] // Empty whitelist = nothing syncs
};Dynamic Strategy Selection
// syncStrategies.js
export const strategies = {
basicCounter: {
channelName: 'basic-counter',
whitelist: ['counter/setIncrement', 'counter/setDecrement']
},
syncEverything: {
channelName: 'sync-all'
},
noSync: {
channelName: 'no-sync',
whitelist: []
}
};
export const getStrategy = (strategyName) => {
return strategies[strategyName] || strategies.basicCounter;
};
// In your store.js
import { getStrategy } from './config/syncStrategies';
const config = getStrategy('basicCounter'); // Easy switching!🎯 Real-World Examples
E-commerce Shopping Cart
const ecommerceConfig = {
channelName: 'ecommerce-sync',
whitelist: [
'cart/addItem',
'cart/removeItem',
'cart/updateQuantity',
'user/login',
'user/logout'
]
};Multi-tab Dashboard
const dashboardConfig = {
channelName: 'dashboard-sync',
whitelist: [
'filters/apply',
'widgets/add',
'widgets/remove',
'settings/update'
]
};Chat Application
const chatConfig = {
channelName: 'chat-sync',
whitelist: [
'messages/receive',
'users/statusUpdate',
'notifications/add'
]
};📚 API Reference
createSyncStateMiddleware(config?)
Creates Redux middleware for syncing actions across tabs.
interface SyncStateConfig {
channelName?: string; // Custom channel name
whitelist?: string[]; // Action types to sync
}Parameters:
config(optional): Configuration objectchannelName: Custom broadcast channel name (default: 'REDUX_STATE_SYNCING')whitelist: Array of action types to sync. If undefined, syncs all actions. If empty array, syncs nothing.
initSyncStateListener(store, channelName?)
Initializes the broadcast channel listener for receiving actions from other tabs.
Parameters:
store: Redux store instancechannelName: Optional channel name (must match middleware config)
syncStateReducer(reducer)
Higher-order reducer that wraps your existing reducer to handle state synchronization.
Parameters:
reducer: Your original reducer function
Returns: Enhanced reducer with sync capabilities
🔧 Advanced Configuration
Environment-based Setup
const getConfig = () => {
if (process.env.NODE_ENV === 'development') {
return { channelName: 'dev-sync' }; // Sync everything in dev
}
return {
channelName: 'prod-sync',
whitelist: ['user/login', 'cart/update'] // Only essential actions in prod
};
};Multiple Channels for Large Apps
// User authentication channel
const userConfig = {
channelName: 'user-sync',
whitelist: ['user/login', 'user/logout', 'user/updateProfile']
};
// Shopping cart channel
const cartConfig = {
channelName: 'cart-sync',
whitelist: ['cart/addItem', 'cart/removeItem', 'cart/updateQuantity']
};
const store = configureStore({
reducer: {
user: syncStateReducer(userReducer),
cart: syncStateReducer(cartReducer),
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware()
.concat(createSyncStateMiddleware(userConfig))
.concat(createSyncStateMiddleware(cartConfig)),
});
// Initialize both listeners
initSyncStateListener(store, userConfig.channelName);
initSyncStateListener(store, cartConfig.channelName);🎨 Use Cases
✅ Perfect For:
- Shopping carts - Keep items synced across tabs
- User authentication - Login/logout in all tabs simultaneously
- Theme preferences - Dark/light mode sync
- Form drafts - Auto-save progress across tabs
- Dashboard filters - Maintain consistent view state
- Real-time notifications - Show updates in all tabs
- Multi-step workflows - Continue where you left off
❌ Not Recommended For:
- Large datasets (>1MB) - Use server-side sync instead
- High-frequency updates (>100/sec) - May impact performance
- Sensitive data - Consider security implications
- Tab-specific UI state - Modal open/close, scroll positions
🐛 Troubleshooting
Actions not syncing between tabs?
Check channel names match:
const channelName = 'my-app-sync'; createSyncStateMiddleware({ channelName }); initSyncStateListener(store, channelName);Verify action types in whitelist:
// ✅ Correct - include slice name whitelist: ['counter/setIncrement'] // ❌ Wrong - missing slice name whitelist: ['setIncrement']Check browser console for errors
Performance issues?
Use selective sync:
// Instead of syncing everything
createSyncStateMiddleware()
// Sync only what you need
createSyncStateMiddleware({
whitelist: ['user/login', 'cart/update']
})Browser compatibility?
- ✅ Chrome 54+
- ✅ Firefox 38+
- ✅ Safari 10+
- ✅ Edge 79+
- ❌ Internet Explorer (not supported)
🔒 Security Considerations
- Data is shared locally between tabs of the same origin only
- No data leaves the user's browser
- Consider what state you sync (avoid sensitive data)
- Use HTTPS in production for additional security
📈 Performance Tips
- Use whitelists to sync only necessary actions
- Separate channels for different features
- Avoid syncing high-frequency actions (animations, scroll events)
- Test with multiple tabs to ensure smooth performance
🤝 Contributing
We welcome contributions! Please feel free to submit issues and pull requests.
# Clone the repository
git clone https://github.com/MAsad961/redux-tab-connect.git
# Install dependencies
npm install
# Build the package
npm run build
# Test with the example client
cd client && npm start📄 License
MIT © Asad
🙏 Support
If this package helped you:
- ⭐ Star it on GitHub
- 🐛 Report issues or bugs
- 💡 Suggest new features
- 📝 Help improve documentation
Made with ❤️ for the Redux community
Need help? Open an issue
