As an app grows, it collects contexts: a theme, a locale, the signed-in user, and more. Nesting each provider by hand turns the root component into a deep pyramid that gets harder to read with every new context. You can combine multiple context providers into one component to keep the tree flat and the wiring in one place.
The nested pyramid problem
Each context needs its own provider wrapper, so a root component ends up indenting one provider inside another. Three shared values already produce three levels of nesting.
export default function App() {
return (
<ThemeProvider>
<UserProvider>
<LocaleProvider>
<Page />
</LocaleProvider>
</UserProvider>
</ThemeProvider>
);
}This works, but every new context adds another layer of indentation. The provider order is also easy to lose in the nesting, which matters when one provider depends on another.
Combine the providers
Move the nesting into one component that accepts children and renders them inside every provider. The component does nothing but compose providers.
export function AppProviders({ children }) {
return (
<ThemeProvider>
<UserProvider>
<LocaleProvider>{children}</LocaleProvider>
</UserProvider>
</ThemeProvider>
);
}AppProviders owns the nesting so no other file has to think about it. When a fourth context appears, you edit this one component and every consumer keeps working.
Use it at the root
Wrap the app in AppProviders and pass the page as children. The root component stays flat no matter how many providers sit behind the scenes.
import { AppProviders } from "./providers";
export default function App() {
return (
<AppProviders>
<Page />
</AppProviders>
);
}The page and its descendants can read any of the three contexts. The root no longer reveals which contexts exist, which makes the structure easier to scan.
Provider order matters
A provider only serves the tree below it, so order matters only when one provider reads from another. If LocaleProvider needs the current user, it must sit inside UserProvider.
export function AppProviders({ children }) {
return (
<UserProvider>
<LocaleProvider>{children}</LocaleProvider>
</UserProvider>
);
}Here LocaleProvider can read the user from UserProvider. Swap the order and it would fall back to the default value instead, because UserProvider would not be above it.
Keep providers scoped
Not every provider must wrap the whole app. A theme provider belongs at the root, but a provider that serves one form or one dashboard section can wrap just that part of the tree. Scoping a provider limits re-renders to the components that actually read the value, so a change does not wake up the entire app.
When one file gets crowded
A single providers file works until it holds too much. When each provider starts needing its own state and update logic, split them into separate modules and import them into the combiner. See how to split React context by responsibility for the read and write split that keeps each context small.
What to learn next
If the shared values start to outgrow simple context, compare the options in context vs Redux vs Zustand.
Rune AI
Key Insights
- Extract one component that composes all providers.
- The component accepts children and renders them inside every provider.
- The root then wraps the page once instead of nesting providers.
- Order matters only when one provider reads from another.
- Scope providers to the part of the tree that needs them.
Frequently Asked Questions
Do multiple providers conflict with each other?
Should every provider wrap the whole app?
What does the combined provider component accept?
Conclusion
Combine multiple context providers by extracting one provider component that accepts children and renders them inside every provider. Order the providers so any provider that reads another sits below it, and keep the root component flat.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.