Skip to content
👉All tips here

React Providers Composition: From Pyramid to Clean Code

React compose multiple providers frontend tips

Have you ever found yourself staring at a React providers composition that looks like a pyramid? I sure have! Let me share a neat trick I use to clean up those nested providers and make my code more readable.

If you’re building React apps, you’ve probably used Context providers. They’re super helpful for sharing state across your app – things like:

  • User authentication state
  • Theme preferences
  • Language settings
  • API client configuration

Here’s what it typically looks like when you start a new project:

const App = () => {
  return (
    <ThemeProvider theme={darkTheme}>
      <AuthProvider>
        <NotificationProvider>
          <UserPreferencesProvider>
            <LayoutProvider>
              {/* Your actual app content */}
              <HomePage />
            </LayoutProvider>
          </UserPreferencesProvider>
        </NotificationProvider>
      </AuthProvider>
    </ThemeProvider>
  );
};

Looks familiar? Yeah, not the prettiest sight! 😅 It’s like those nested divs we all try to avoid. Let’s fix this!

A Better React Providers Composition Pattern

Here’s a simple but powerful pattern I love using to clean this up. Instead of nesting multiple providers, we can compose them into a single, clean component.

First, let’s create a function that composes our providers:

const composeProviders = (providers) =>
  providers.reduce(
    (Accumulated, Current) =>
      ({ children }) =>
        (
          <Accumulated>
            <Current>{children}</Current>
          </Accumulated>
        )
  );

Now, let’s see how we use this. First, we’ll create our providers with their configurations:

// providers/appProviders.jsx
import { ThemeProvider } from './ThemeProvider';
import { AuthProvider } from './AuthProvider';
import { NotificationProvider } from './NotificationProvider';
import { UserPreferencesProvider } from './UserPreferencesProvider';
import { LayoutProvider } from './LayoutProvider';

// Create providers with their respective configs
const createProviders = (config) => [
  (props) => <ThemeProvider theme={config.theme} {...props} />,
  AuthProvider,
  NotificationProvider,
  UserPreferencesProvider,
  LayoutProvider,
];

// Compose them into a single provider component
export const AppProviders = ({ config, children }) => {
  const providers = createProviders(config);
  const ComposedProviders = composeProviders(providers);
  
  return <ComposedProviders>{children}</ComposedProviders>;
};

And here’s how our App component looks now – so much cleaner, and easier to read!

const App = () => {
  const config = {
    theme: darkTheme,
    // other config options
  };

  return (
    <AppProviders config={config}>
      <HomePage />
    </AppProviders>
  );
};

Benefits of React Providers Composition

  1. Cleaner Code: No more provider pyramids!
  2. Easy to Maintain: Need to add a new provider? Just add it to the array.
  3. Configuration in One Place: All provider configs can be managed together.
  4. Reusable: You can create different provider compositions for different parts of your app.

That’s my little trick for tidier provider code, so you can now look at your component tree without getting dizzy! Hope this helps you as much as it’s helped me 🙂

Learn more: