@evanion/compose
@evanion/compose 2.1.0 and its patch releases up to 2.1.1, the version on npm. Values on this page were produced by running the source at 77650ee, a later commit than the 2.1.0 tag, and are not re-executed.
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
AuthProvideraboveThemeProvider) 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
providersarray. - 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]. Theprovider()helper is used to create these tuples while ensuring prop safety at compile time.
Core Capabilities
| Goal | Tool |
|---|---|
| Organize root providers as a list | ComposeProvider |
| Assign props to a provider | A tuple or provider(component, props) |
| Catch prop errors at compile time | Inline arrays or as const declarations |
| Preserve type checks in wrappers | ComposeProviderProps<T> with a const type parameter |
| Use in React Server Components | ComposeProvider (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:
ComposeProvideris 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.