Skip to Content
ComposeOverview

Unreleased changes

These pages document main, which has changes 2.1.1 does not. npm install @evanion/compose gives you 2.1.1, documented here. Values on these pages are produced by running the current source on every build.

@evanion/compose

Escape “Provider Hell” with a flat, type-safe way to organize your React context providers.

In a traditional React application, as you add more features, your root component becomes a “pyramid of doom”—a deeply nested stack of providers that is hard to maintain, painful to reorder, and a nightmare to indent.

@evanion/compose solves this by allowing you to define your providers as a flat array. It then nests them automatically, moving the complexity out of your JSX and into a clean, manageable list.

The Problem: The “Pyramid of Doom”

Manual nesting of providers creates several maintenance burdens:

  • Indentation Drift: Adding a new provider at the top requires re-indenting every single child component.
  • Closing Tag Fatigue: Finding the matching </ThemeProvider> among ten other closing tags is error-prone.
  • Reordering Friction: Moving a provider (e.g., moving AuthProvider above ThemeProvider) requires carefully cutting and pasting large blocks of JSX.

The Solution: Flat Composition

ComposeProvider accepts a flat array of providers and handles the nesting for you. It reads the array from top to bottom: the first provider becomes the outermost wrapper, and the last provider wraps the children.

const = ({ }: React.) => ( < ="cart">{}</> ); const = ({ , , }: React.<{ : 'light' | 'dark' }>) => ( < ={}>{}</> ); const = ( < ={[, (, { : 'dark' })]} > <>Brass: Birmingham</> </>, ); ; // -> '<div id="cart"><div id="dark"><p>Brass: Birmingham</p></div></div>'

By treating your providers as data (a list) rather than as structural JSX, you can reorder your entire app’s context hierarchy just by moving a line in an array.

RSC Compatibility

Because ComposeProvider does not call hooks or create its own context, it is fully compatible with React Server Components (RSC). You can use it to organize your server-side provider tree without triggering “use client” errors.

The Compose Vocabulary

To keep the documentation clear, we use these terms to describe the composition process:

  • Provider: Any React component that renders its children.
  • Entry: A single element within the providers array.
  • Bare Component: An entry written as the component itself (e.g., CartProvider). This is used for providers whose props are all optional.
  • Tuple: An entry pairing a component with its props, [component, props]. The provider() helper is used to create these tuples while ensuring prop safety at compile time.

Core Capabilities

GoalTool
Organize root providers as a listComposeProvider
Assign props to a providerA tuple or provider(component, props)
Catch prop errors at compile timeInline arrays or as const declarations
Preserve type checks in wrappersComposeProviderProps<T> with a const type parameter
Use in React Server ComponentsComposeProvider (no hooks/context used)

When to Avoid Compose

While powerful, ComposeProvider is not the right tool for every scenario:

  • Minimal Nesting: If you only have one or two providers, manual nesting is often more readable.
  • Dynamic Providers: If the number or order of providers changes at runtime, React will remount the entire subtree, causing all providers to lose their state.
  • Side-by-Side Layouts: ComposeProvider is for nesting, not layout. If you need to arrange components side-by-side based on data, use @evanion/react-widget.

Get Started with a Shop Root

A @evanion/compose array is a top-to-bottom map of your component tree. Getting started shows you how to install the package and build a root for a board game shop using three different context providers.

Last updated on