npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mantaray0/provider-stack

v1.0.0

Published

Compose React providers into a clean, configurable stack

Readme

@mantaray0/provider-stack

A lightweight utility to compose React providers into a clean, configurable stack. Works with any React framework including Next.js, Remix, Gatsby, Vite, Create React App, and any other React-based application.

Features

  • Lightweight: Minimal bundle size with zero runtime dependencies
  • Flexible: Support for wrapping and standalone providers
  • Type Safe: Full TypeScript support
  • Simple API: Clean syntax for provider composition
  • Order Control: Maintain full control over provider nesting order
  • Two Provider Types: Wrapping providers (wrap children) and standalone providers (sibling to children)

Installation

npm install @mantaray0/provider-stack
# or
pnpm add @mantaray0/provider-stack
# or
yarn add @mantaray0/provider-stack

What is this package?

When building React applications, you often need multiple context providers (Theme, Auth, i18n, etc.). Without this package, your code can become messy with deeply nested provider components:

// Without ProviderStack - messy nesting
<ThemeProvider theme="dark">
  <AuthProvider session={session}>
    <QueryClientProvider client={queryClient}>
      <App />
      <ToastContainer position="top-right" />
      <ShortcutHint />
    </QueryClientProvider>
  </AuthProvider>
</ThemeProvider>

With @mantaray0/provider-stack, you can compose all providers into a clean, declarative array:

import { ProviderStack, standalone, ProviderDefinition } from '@mantaray0/provider-stack';

// With ProviderStack - clean and maintainable
const providers: ProviderDefinition[] = [
  // Wrapping providers (wrap children)
  [ThemeProvider, { theme: 'dark' }],
  [AuthProvider, { session }],
  [QueryClientProvider, { client: queryClient }],

  // Standalone providers (rendered as siblings to children)
  standalone(ToastContainer, { position: 'top-right' }),
  standalone(ShortcutHint),
];

<ProviderStack providers={providers}>
  <App />
</ProviderStack>;

Usage

Basic Setup

Import ProviderStack and define your providers as an array:

import { ProviderStack, standalone, ProviderDefinition } from '@mantaray0/provider-stack';
import { ThemeProvider } from './providers/ThemeProvider';
import { AuthProvider } from './providers/AuthProvider';
import { ToastContainer } from './providers/ToastContainer';

function App() {
  const providers: ProviderDefinition[] = [
    // Wrapping providers
    [ThemeProvider, { theme: 'dark' }],
    [AuthProvider, { session }],

    // Standalone providers
    standalone(ToastContainer, { position: 'top-right' }),
  ];

  return (
    <ProviderStack providers={providers}>
      <YourApp />
    </ProviderStack>
  );
}

Provider Types

There are two types of providers:

1. Wrapping Providers

Wrapping providers wrap the children, like <Provider>{children}</Provider>. They can have props or not:

const providers = [
  // With props
  [ThemeProvider, { theme: 'dark' }],
  [AuthProvider, { session, onSignOut: handleSignOut }],

  // Without props
  FeatureProvider,
  [FeatureProvider], // Also valid
];

2. Standalone Providers

Standalone providers are rendered as siblings to the children, like <Provider />. Use the standalone() helper:

import { standalone } from '@mantaray0/provider-stack';

const providers = [
  // With props
  standalone(ToastContainer, { position: 'top-right' }),
  standalone(ModalPortal, { container: document.body }),

  // Without props
  standalone(ShortcutHint),
  standalone(DevTools),
];

Syntax Reference

Wrapping Providers

When you need to pass configuration props to a provider, use the tuple syntax [Component, props]:

const providers = [
  [ThemeProvider, { theme: 'dark', colorScheme: 'system' }],
  [AuthProvider, { session, onSignOut: handleSignOut }],
  [QueryClientProvider, { client: queryClient }],
];

Syntax:

  • [Component, { prop1: value1, prop2: value2 }] - Component with props object

Important Note on Boolean Props:

When using boolean properties in the array syntax, you must explicitly set them to true. Unlike JSX where you can write <ThemeProvider enableSystem />, in the array syntax you need to write enableSystem: true:

// In JSX (regular React):
<ThemeProvider enableSystem disableTransitionOnChange otherProperty="value" />

// In array syntax (ProviderStack):
const providers = [
  [ThemeProvider, { 
    enableSystem: true,  // Must explicitly set to true
    disableTransitionOnChange: true,  // Must explicitly set to true
    otherProperty: "value" 
  }]
];

Example:

// ThemeProvider accepts theme prop
const ThemeProvider = ({ theme, children }) => (
  <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
);

// Usage with props
const providers = [[ThemeProvider, { theme: 'dark' }]];

// Even if ThemeProvider has optional props, you can still use it without props:
const providers = [
  ThemeProvider, // Uses default/optional props
];

2. Provider without Props

When a provider doesn't need props (or uses default/optional props), you can pass it directly:

const providers = [FeatureProvider, ToastProvider, ModalProvider];

Syntax:

  • Component - Component without props (uses default/optional props if any)
  • [Component] - Also valid (tuple without props object)

Important: A provider passed without props can still accept props if they are optional. The difference is only in the syntax used:

// FeatureProvider that accepts optional props
const FeatureProvider = ({ enabled = true, children }) => {
  return <FeatureContext.Provider value={{ enabled }}>{children}</FeatureContext.Provider>;
};

// Usage - all three are equivalent:
const providers = [
  FeatureProvider, // Uses default props (enabled = true)
  [FeatureProvider], // Same as above
  [FeatureProvider, { enabled: false }], // Explicitly passes props
];

3. Provider with Empty Props Object

You can also explicitly pass an empty props object:

const providers = [
  [FeatureProvider, {}], // Explicitly no props
];

This is equivalent to FeatureProvider or [FeatureProvider].

Complete Example

Here's a complete example showing all provider types:

import { ProviderStack, standalone, ProviderDefinition } from '@mantaray0/provider-stack';
import { ThemeProvider } from './providers/ThemeProvider';
import { UserProvider } from './providers/UserProvider';
import { FeatureProvider } from './providers/FeatureProvider';
import { ToastContainer } from './components/ToastContainer';
import { ShortcutHint } from './components/ShortcutHint';

function App() {
  const providers: ProviderDefinition[] = [
    // WRAPPING PROVIDERS - wrap the children
    [ThemeProvider, { theme: 'dark' }], // With props
    [UserProvider, { name: 'John Doe' }], // With props
    FeatureProvider, // Without props

    // STANDALONE PROVIDERS - rendered as siblings to children
    standalone(ToastContainer, { position: 'top-right' }), // With props
    standalone(ShortcutHint), // Without props
  ];

  return (
    <ProviderStack providers={providers}>
      <YourApp />
    </ProviderStack>
  );
}

This results in the following structure:

<ThemeProvider theme="dark">
  <UserProvider name="John Doe">
    <FeatureProvider>
      <YourApp />
      <ToastContainer position="top-right" />
      <ShortcutHint />
    </FeatureProvider>
  </UserProvider>
</ThemeProvider>

Key Points:

  • Wrapping providers use [Component, props] or just Component
  • Standalone providers use standalone(Component, props) or standalone(Component)
  • Wrapping providers wrap all content (including standalone providers)
  • Standalone providers are rendered as siblings to your children

Provider Order

The order in the array determines the nesting structure:

// This configuration:
const providers = [A, B, C];

// Results in:
<A>
  <B>
    <C>{children}</C>
  </B>
</A>;

Standalone providers respect their position in the array:

const providers = [
  [ThemeProvider, { theme: 'dark' }], // Wrapping
  standalone(ToastContainer), // Standalone - at this level
  [AuthProvider], // Wrapping
];

// Results in:
<ThemeProvider theme="dark">
  <ToastContainer />
  <AuthProvider>{children}</AuthProvider>
</ThemeProvider>;

The ToastContainer is rendered as a sibling to AuthProvider inside ThemeProvider, exactly where it's positioned in the array.

Important: Order matters! Providers listed first wrap providers listed later. Standalone providers become siblings at their position.

Advanced Usage

Pre-configured Provider Stack

Use createProviderStack to create a reusable provider stack:

// providers.tsx
import { createProviderStack, standalone } from '@mantaray0/provider-stack';

export const AppProviders = createProviderStack([
  [ThemeProvider, { theme: 'dark' }],
  [QueryClientProvider, { client: queryClient }],
  FeatureProvider,
  standalone(ToastContainer),
]);

// layout.tsx or _app.tsx
import { AppProviders } from './providers';

export default function RootLayout({ children }) {
  return <AppProviders>{children}</AppProviders>;
}

Combining Provider Arrays

Use combineProviders to organize providers by concern:

import {
  combineProviders,
  ProviderStack,
  standalone,
  ProviderDefinition,
} from '@mantaray0/provider-stack';

// Organize providers by concern
const coreProviders: ProviderDefinition[] = [
  [ThemeProvider, { theme }],
  [I18nProvider, { locale }],
];

const dataProviders: ProviderDefinition[] = [
  [QueryClientProvider, { client: queryClient }],
  [AuthProvider, { session }],
];

const uiProviders: ProviderDefinition[] = [
  standalone(ToastContainer, { position: 'top-right' }),
  standalone(ModalPortal),
];

// Combine them
const allProviders = combineProviders(coreProviders, dataProviders, uiProviders);

<ProviderStack providers={allProviders}>
  <App />
</ProviderStack>;

Framework Integration

This package works with any React framework. Here are examples for popular frameworks:

Next.js

App Router (Next.js 13+)

With the App Router, provider definitions must be in a Client Component since functions cannot be passed from Server to Client Components.

// app/providers.tsx
'use client';

import { ProviderStack, standalone, ProviderDefinition } from '@mantaray0/provider-stack';
import { ThemeProvider } from './providers/ThemeProvider';
import { AuthProvider, Session } from './providers/AuthProvider';
import { QueryClientProvider, queryClient } from './providers/QueryClientProvider';
import { ToastContainer } from './components/ToastContainer';

interface AppProvidersProps {
  children: React.ReactNode;
  session: Session;
}

export const AppProviders = ({ children, session }: AppProvidersProps) => {
  const providers: ProviderDefinition[] = [
    [ThemeProvider, { theme: 'dark' }],
    [AuthProvider, { session }],
    [QueryClientProvider, { client: queryClient }],
    standalone(ToastContainer, { position: 'top-right' }),
  ];

  return <ProviderStack providers={providers}>{children}</ProviderStack>;
};
// app/layout.tsx
import { AppProviders } from './providers';
import { getSession } from './auth';

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const session = await getSession();

  return (
    <html lang="en">
      <body>
        <AppProviders session={session}>{children}</AppProviders>
      </body>
    </html>
  );
}

This pattern allows you to pass dynamic data from Server Components (like user session, translations, etc.) to your Client Component providers.

Pages Router

// pages/_app.tsx
import { ProviderStack, standalone, ProviderDefinition } from '@mantaray0/provider-stack';
import type { AppProps } from 'next/app';

const providers: ProviderDefinition[] = [
  [ThemeProvider, { theme: 'dark' }],
  [AuthProvider, { session }],
  [QueryClientProvider, { client: queryClient }],
  standalone(ToastContainer, { position: 'top-right' }),
];

export default function App({ Component, pageProps }: AppProps) {
  return (
    <ProviderStack providers={providers}>
      <Component {...pageProps} />
    </ProviderStack>
  );
}

Remix

// app/root.tsx
import { ProviderStack, standalone, ProviderDefinition } from '@mantaray0/provider-stack';

const providers: ProviderDefinition[] = [
  [ThemeProvider, { theme: 'dark' }],
  [AuthProvider, { session }],
  [QueryClientProvider, { client: queryClient }],
  standalone(ToastContainer, { position: 'top-right' }),
];

export default function App() {
  return (
    <html>
      <body>
        <ProviderStack providers={providers}>
          <Outlet />
        </ProviderStack>
      </body>
    </html>
  );
}

Vite / Create React App

// src/main.tsx or src/index.tsx
import { ProviderStack, standalone, ProviderDefinition } from '@mantaray0/provider-stack';
import React from 'react';
import ReactDOM from 'react-dom/client';

const providers: ProviderDefinition[] = [
  [ThemeProvider, { theme: 'dark' }],
  [AuthProvider, { session }],
  [QueryClientProvider, { client: queryClient }],
  standalone(ToastContainer, { position: 'top-right' }),
];

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <ProviderStack providers={providers}>
      <App />
    </ProviderStack>
  </React.StrictMode>
);

Gatsby

// gatsby-browser.js or gatsby-ssr.js
import { ProviderStack, standalone, ProviderDefinition } from '@mantaray0/provider-stack';
import React from 'react';

const providers: ProviderDefinition[] = [
  [ThemeProvider, { theme: 'dark' }],
  [AuthProvider, { session }],
  [QueryClientProvider, { client: queryClient }],
  standalone(ToastContainer, { position: 'top-right' }),
];

export const wrapRootElement = ({ element }) => (
  <ProviderStack providers={providers}>{element}</ProviderStack>
);

Any React Application

Since this package is framework-agnostic, you can use it anywhere React components are used:

import { ProviderStack, standalone, ProviderDefinition } from '@mantaray0/provider-stack';

const providers: ProviderDefinition[] = [
  [ThemeProvider, { theme: 'dark' }],
  [AuthProvider, { session }],
  [QueryClientProvider, { client: queryClient }],
  standalone(ToastContainer, { position: 'top-right' }),
];

function App() {
  return (
    <ProviderStack providers={providers}>
      <YourApp />
    </ProviderStack>
  );
}

API Reference

ProviderStack

The main component that wraps your application with multiple providers.

Props

| Prop | Type | Required | Description | | --------- | -------------------- | -------- | -------------------------------------- | | providers | ProviderDefinition[] | Yes | Array of providers (first = outermost) | | children | ReactNode | No | Your application components |

ProviderDefinition

A provider can be defined in several ways:

// Wrapping providers (wrap children)
type WrapperDef =
  | ComponentType                    // Provider without props
  | [ComponentType]                  // Provider without props (tuple form)
  | [ComponentType, Props]           // Provider with props

// Standalone providers (sibling to children)
type StandaloneDef = standalone(Component) | standalone(Component, props)

type ProviderDefinition = WrapperDef | StandaloneDef

standalone

Helper function to create a standalone provider definition:

import { standalone } from '@mantaray0/provider-stack';

// Without props
standalone(ToastContainer);

// With props
standalone(ToastContainer, { position: 'top-right' });

Standalone providers are rendered as siblings to the children, not wrapping them.

createProviderStack

Factory function to create a pre-configured ProviderStack component.

const AppProviders = createProviderStack([
  [ThemeProvider, { theme: 'dark' }],
  FeatureProvider,
  standalone(ToastContainer),
]);

<AppProviders>
  <App />
</AppProviders>;

combineProviders

Utility to combine multiple provider arrays.

const allProviders = combineProviders(coreProviders, dataProviders, standaloneProviders);

TypeScript Support

Full TypeScript support is included:

import { ProviderDefinition, standalone } from '@mantaray0/provider-stack';

const providers: ProviderDefinition[] = [
  // Wrapping providers
  [ThemeProvider, { theme: 'dark' }],
  FeatureProvider,

  // Standalone providers
  standalone(ToastContainer, { position: 'top-right' }),
  standalone(ShortcutHint),
];

Migration

This package was previously published under different names. If you are migrating from an older version, update your package name and imports:

| Old package name | New package name | |---|---| | @beluga-labs/provider-stack | @mantaray0/provider-stack |

# Remove the old package
npm uninstall @beluga-labs/provider-stack

# Install the new package
npm install @mantaray0/provider-stack

Then update all imports:

- import { ProviderStack, standalone } from '@beluga-labs/provider-stack';
+ import { ProviderStack, standalone } from '@mantaray0/provider-stack';

The API remains unchanged — no code changes beyond the import paths are required.

Contributing

We welcome contributions! Please see our contributing guidelines for more details.

License

MIT License - see LICENSE file for details.