As developers, we experience such scenarios in our daily coding sessions, where extremely abstract and scattered React context logics can quickly become unmanageable when used under one provider.
The problem
I was the one who dealt with a core provider logic involving five or more nested contexts mixed together. The project was pretty old and odd 🙃 but I still had to take action on the contexts. Hence, I checked the inner functionality of each, which resulted in me wasting hours as the inner logic concluded that the functions on the other side needed to be analyzed.
In my case, the intention to check wasn't arbitrary though, there was race-condition between the providers and I was pretty sure that it was about the ordering.
How long did it take to fix?
At first, I thought to myself, "Of course it's not going to take long to figure this out", but it turned out to be "Claude, reorder the providers"..
Showcasing a Pyramid of React Context Provider
Sometimes, the providers are not designed only to be responsible with passing the props using React states. We also utilize them to derive or calculate with utility functions before passing to the context's state during the hydration or later, depending on the circumstances.
In my scenario, the provider was likely a candidate to be Great Pyramid of Giza due to its large amount descending structure. Have a look:
It's a client-side wrapper, acting as a communication channel between the worker and the main thread for UX feedback to keep the user busy with some UI stuffs during the internal worker processes.
Getting rid of '.provider' syntax
The last maintenance was carried out about a year ago, just before React 19 was introduced. So, the newer syntax, the better readability.
The .Provider part of the syntax is now obsolete and redundant. If we trim it out then the code would look like this:
The 'Comment' Bottleneck of Traditional Nested React Contexts Caused by Linters and Prettifiers
I believe this one may likely sound something new. When the providers are being used on the client side with bunch of nested contexts just like the one above, we usually do require comments but since we know it's internals, we're just skipping that part because, why not ✨
However, if you're the one working with a team, you might need to let the other developers know the purpose of the contexts unless the naming convention is clear enough or the context is pretty straightforward. Then I assure you, the linters will be problematic.
No matter using biome or eslint with prettier. When you add a comment and save the file, your existing formatter may relocate the comment you made, which could cause an error, syntactically.

There is a workaround to prevent this linter/formatter behavior when the file saved, it's ugly but at least something better:

And here is the solution that we're about to see to all these junky formatting behaviors:

The Solution with Composers
Starting with creating a new file called composeProviders.tsx with .tsx extension and place it inside your lib folder or wherever you think is suitable.
The .tsx extension is mandatory and not arbitrary as we're working with the React components. Don't forget to take the difference into account.
Writing our TypeScript Types
First, we're gonna need to create a type to let the composer know that we're working explicitly with React components. So our providers's type can be inferred by the composer.
The reason we used unknown for the type of context value is that the composer can accept any kind of value that might be controlled inside of the file where it's being passed whether via satisfies or as. I know it's less type-safe but you can use generic types to achieve 1o1 type inferring by simply converting the Provider type to a generic type.
never is used in the tuple on purpose: ComponentType<{ value: never }> accepts any provider (parameter contravariance). But then we need a second, wider type for rendering.
I wrote a comprehensive deep dive in my earlier articles. If you feel you lack knowledge about writing generic types, I definitely recommend checking it out 🚀
Our context type ready. Now let's design our entry point for the composer because we're gonna need to work with mapped arrays to be able to generate the composition for the React contexts.
Now the StoredProvider indicates that we're solely allowing 3 arguments in a tuple array. readonly determines that we have nothing to do other than passing the undergoing array items to the subsequent action or wherever it goes.
Consider that the readonly type modifier is only permitted on array and tuple literal types. So we can't use it for the ContextProvider
Implementing The Logic
We're gonna need something that properly handles the order of the contexts properly as well as slightly different approach than the traditional array mapping mechanism. Unlike the traditional method, it should not use intermediate JavaScript methods with multiple maps and sorts in terms of performance and readability.
Meet with reduceRight(). It's viable particularly for this implementation due to it's mapping all the elements in an array, in descending order.
So everything turns out to this when wrapped altogether:
Breaking down in order:
- Takes multiple [Provider, value, key] entries.
- Wraps children with every provider automatically.
- Uses reduceRight() to preserve provider nesting order.
- The first provider in the array becomes the outermost provider.
Implementation on our React Components
Everything we've done so far was to be able to minimize the complication of pyramid effect and nothing is harder than it's implementation. We're gonna only need to compose and get rid of the pyramid like this:
Bonus (Type Safe Approach)
Remember that I mentioned earlier that we can map our types out strictly? So basically with the previous implementation, a mismatched pair compiles and fails at runtime.
As an alternative here is a generic factory removes the cast and type checks each provider/value pair at the call site:
Implementation fairly longer but better:
Now, in order to test it out, as instance let's deliberately mistaken the passed state using the stream.status for StreamMetaContext like this:
yields a type error at compile time:
The error in the IDE looks like this:

Ultimately, it's much safer and solid path to go with in prod-grade apps without concerning about runtime/compile issues.
Conclusion
The takeaways already given above so It appears to me that there is no more saying to be added below here😀. Of course you can utilize state managers like Redux or Zustand for smaller implementations depending on the use case and not tangle with any of this but if you think it's an overhead and willing to stay native to React, here is the solution for all that spaghetti or pyramid context chains.
Thanks for reading.
It's again a late night, 02:27 AM.
