How Multiple Contexts Organize Application State
Multiple Contexts pattern separates concerns by creating dedicated contexts for different domains of application state. This approach prevents context value bloat while enabling components to subscribe only to relevant data updates, leading to better performance and more maintainable codebases.
TL;DR
- Use separate contexts for different state domains
- Multiple Contexts prevents unnecessary re-renders
- Enables granular control over state subscriptions
- Perfect for large applications with complex state
const result = process(data)
The Monolithic Context Challenge
Your application stores all state in a single massive context object. Components re-render unnecessarily when unrelated state changes, and the context provider becomes a bottleneck. Adding new features requires modifying the central context structure.
// Single context with all application state
const React = require('react')
const AppContext = React.createContext()
function AppProvider({ children }) {
const state = {
user: { name: 'John', role: 'admin' },
theme: 'dark',
cart: { items: [], total: 0 },
}
console.log('Monolithic context causes unnecessary re-renders')
return React.createElement(AppContext.Provider, { value: state }, children)
}
console.log('AppProvider created with single monolithic context')
AppProvider({ children: null })
Multiple Contexts separate concerns by domain, allowing components to subscribe only to relevant updates:
// Separate contexts for different domains
const React = require('react')
const UserContext = React.createContext()
const ThemeContext = React.createContext()
const CartContext = React.createContext()
function CombinedProvider({ children }) {
const user = { name: 'John', role: 'admin' }
const theme = 'dark'
console.log('Multiple contexts enable selective subscriptions')
return React.createElement(
UserContext.Provider,
{ value: user },
React.createElement(ThemeContext.Provider, { value: theme }, children)
)
}
Best Practises
Use multiple contexts when:
- ✅ Different state domains update at different frequencies
- ✅ Components need to subscribe to specific state slices only
- ✅ Building large applications with complex state management
- ✅ Want to avoid unnecessary re-renders from unrelated state changes
Avoid when:
- 🚩 Application has simple, tightly-coupled state
- 🚩 All state updates happen simultaneously
- 🚩 Provider nesting becomes too deep (use composition)
- 🚩 Contexts have frequent cross-dependencies
System Design Trade-offs
Aspect | Multiple Contexts | Single Context |
---|---|---|
Performance | Excellent - selective re-renders | Poor - all consumers re-render |
Maintainability | High - domain separation | Low - monolithic state |
Complexity | Medium - provider composition | Low - single provider |
Testing | Easy - isolate domain contexts | Hard - test entire state |
Scalability | Excellent - add contexts independently | Poor - single point of change |
Re-usability | High - contexts are focused | Low - tightly coupled state |
More Code Examples
❌ Monolithic context anti-pattern
// Single massive context causing performance issues
const React = require('react')
const { createContext } = React
const AppContext = createContext()
function AppProvider({ children }) {
// Simulate problematic monolithic state
const appState = {
user: { id: 1, name: 'Alice', preferences: { theme: 'dark' } },
products: [{ id: 1, name: 'Laptop', price: 999 }],
cart: { items: [], total: 0 },
ui: { loading: false, modal: null },
notifications: [],
}
console.log('AppProvider re-rendering with all state')
const updateUser = (updates) => {
console.log('Would update user:', updates)
}
const updateCart = (items) => {
console.log('Would update cart:', items)
}
return React.createElement(
AppContext.Provider,
{ value: { ...appState, updateUser, updateCart } },
children
)
}
// Components re-render when any state changes
function ProductList() {
// Simulate what useContext would provide from monolithic context
const products = [{ id: 1, name: 'Laptop', price: 999 }]
const user = { name: 'Alice' }
console.log('ProductList re-rendered due to user change')
const items = products.map((p) =>
React.createElement('div', { key: p.id }, p.name + ': $' + p.price)
)
return React.createElement('div', null, items)
}
console.log('Single context causes cascade re-renders')
ProductList() // Test the component
✅ Domain separation with contexts
// Separated contexts for better performance and maintainability
const React = require('react')
const { createContext } = React
// Domain-specific contexts
const UserContext = createContext()
const ProductContext = createContext()
const CartContext = createContext()
// Individual providers
function UserProvider({ children }) {
const user = { id: 1, name: 'Alice', preferences: { theme: 'dark' } }
console.log('UserProvider state:', user.name)
return React.createElement(UserContext.Provider, { value: user }, children)
}
function ProductProvider({ children }) {
const products = [
{ id: 1, name: 'Laptop', price: 999 },
{ id: 2, name: 'Mouse', price: 29 },
]
console.log('ProductProvider has', products.length, 'products')
return React.createElement(ProductContext.Provider, { value: products }, children)
}
// Component only re-renders when products change
function ProductList() {
// Simulate what useContext would provide from ProductContext
const products = [{ id: 1, name: 'Laptop', price: 999 }]
console.log('ProductList rendering products only')
const items = products.map((p) =>
React.createElement('div', { key: p.id }, p.name + ': $' + p.price)
)
return React.createElement('div', null, items)
}
// Root App with composed providers
function App() {
return React.createElement(
UserProvider,
null,
React.createElement(ProductProvider, null, React.createElement(ProductList))
)
}
console.log('Multiple contexts prevent unnecessary re-renders')
console.log('Each context handles one domain efficiently')
console.log('Components subscribe only to relevant data')
ProductList() // Test the component
Technical Trivia
The Slack Performance Investigation of 2020: Slack's engineering team discovered that their React app was re-rendering thousands of components unnecessarily. The culprit was a single massive context containing user data, workspace settings, and channel information all in one object.
Why performance degraded: Every keystroke in a message input triggered context updates that caused the entire sidebar, member list, and channel history to re-render. The monolithic context meant components couldn't subscribe to just the data they needed.
The multiple contexts solution: Slack split their state into UserContext, WorkspaceContext, and ChannelContext. Components now re-render only when their specific domain data changes. This reduced unnecessary renders by 85% and significantly improved typing performance in large workspaces.
Master Multiple Contexts: Implementation Strategy
Design contexts around business domains rather than technical layers. Create separate contexts for user data, application settings, and feature-specific state. Use provider composition at the app root and custom hooks to simplify context consumption. This pattern scales well as applications grow and enables teams to work on different domains independently.