Getting started
@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.
In this guide, you will clean up your application’s root component. You’ll learn how to use ComposeProvider to flatten a nest of context providers into a simple list, and how to verify that your providers are receiving the correct props at compile time.
Prerequisites
To follow this guide, ensure you have the following:
- React 18 or 19: The only dependency for
@evanion/compose. - Node 20 or newer.
- An ESM project: Your project must use ES modules.
- TypeScript 5.0 or newer (Optional): Highly recommended to take advantage of the compile-time prop checking.
Install compose
Run the command for your preferred package manager:
npm install @evanion/composeFlatten your provider tree
Instead of nesting providers manually, you can pass them as an array to ComposeProvider. The first item in the array becomes the outermost wrapper, and the last item wraps your application’s children.
In our Baize shop example, the root needs to provide a cart, a theme, and a currency. We use ComposeProvider to orchestrate this:
const = React.(0);
const = React.<'light' | 'dark'>('light');
const = React.<'SEK' | 'GBP' | 'USD'>('SEK');
const = ({ }: React.) => (
<. ={2}>{}</.>
);
const = ({
,
,
}: React.<{ : 'light' | 'dark' }>) => (
<. ={}>{}</.>
);
const = ({
,
,
}: React.<{ : 'SEK' | 'GBP' | 'USD' }>) => (
<. ={}>
{}
</.>
);
function ({ }: React.) {
return (
<
={[
,
(, { : 'dark' }),
(, { : 'SEK' }),
]}
>
{}
</>
);
}
function () {
const = React.();
const = React.();
const = React.();
return < ={}>{`${} games, priced in ${}`}</>;
}
const = (
<>
< />
</>,
);
; // -> '<p class="dark">2 games, priced in SEK</p>'Breakdown of the Setup:
- Bare Components:
CartProvideris passed as a bare component because it doesn’t require any initial props. - Tuples:
ThemeProviderandCurrencyProviderare wrapped in theprovider()helper. This creates a tuple[Component, Props], which allows TypeScript to verify that you’ve provided the correct props for those specific providers. - The Wrapper: The
Shopcomponent now acts as a clean wrapper that passes these providers toComposeProvider.
If you were to render this to HTML, you’d see a perfectly nested structure where CartProvider is on the outside and CurrencyProvider is on the inside, wrapping your application.
Integrating into your App Root
The Shop component is where your provider logic now lives. You should place this component at the very root of your application, typically around your routes or main page layout.
Next.js App Router Tip:
Any file calling createContext must be a Client Component. Mark your provider file with 'use client', define your providers and your Shop wrapper there, and then wrap your layout in app/layout.tsx with <Shop>{children}</Shop>.
Note that ComposeProvider itself is a pure component that doesn’t use hooks, so it doesn’t require the 'use client' directive.
Visualizing the Nesting
Imagine each provider as a box.
- Moving a provider up in the array moves its “box” further outward, wrapping more of the application.
- Moving a provider down moves it further inward, wrapping fewer components.
The interactive example this release showed here runs against the current source, so it is on the same page under /next/ and not on this one.
Warning: Changing the order of providers while the app is running will cause React to remount the entire subtree, which will reset any local state held by those providers.
Catching Prop Errors at Compile Time
One of the biggest advantages of @evanion/compose is that it brings type safety to your provider list. Because we use tuples and the provider() helper, TypeScript can verify that the props you pass to a provider match its requirements. If you pass a string where a number is expected, TypeScript will flag it immediately.
For a detailed look at how this verification works, see Where the check happens.