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

react-context-manager

v1.0.1

Published

Automatic React Context composition with ordering and dependency management.

Readme

react-context-manager

Automatic React Context composition with ordering and dependency management.

react-context-manager makes it easier to manage multiple React Context providers in medium and large React applications.

Instead of manually nesting providers:

<AuthProvider>
	<ConfigProvider>
		<ThemeProvider>
			<UserProvider>
				<App />
			</UserProvider>
		</ThemeProvider>
	</ConfigProvider>
</AuthProvider>

you can register your contexts and let ContextRoot compose them automatically.

<ContextRoot>
	<App />
</ContextRoot>

The library can determine provider order using explicit dependencies and priorities.


Features

  • Automatic composition of multiple React Context providers
  • Dependency-based provider ordering
  • Priority-based ordering for independent providers
  • Support for existing React Contexts
  • Create and register contexts through the package
  • Create contexts and providers together with defineProvider
  • Named context access with useNamedContext
  • Retrieve registered contexts with fetchContext
  • TypeScript support
  • React 18 and React 19 support
  • Provider registration validation
  • Missing dependency detection
  • Circular dependency detection
  • Dynamic provider registration support

Installation

npm install react-context-manager

React must already be installed in your application.

npm install react react-context-manager

Basic Concept

The package uses three main concepts:

defineContext

Register a provider that already exists in your application.

defineContext({
	name: "AuthContext",
	provider: AuthProvider,
});

defineProvider

Create a React Context and its provider together.

const auth = defineProvider({
	name: "AuthContext",
	defaultValue: null,
	useValue: () => null,
});

ContextRoot

Automatically composes all registered providers.

<ContextRoot>
	<App />
</ContextRoot>

Example 1 — Using Existing Contexts From an Existing Application

This example is useful when you already have a React application with contexts and providers.

Suppose your existing application already contains an authentication context.

Existing context

// AuthContext.tsx

import React, { createContext, useContext, useState } from "react";

type User = {
	id: string;
	name: string;
};

type AuthContextValue = {
	user: User | null;
	login: (user: User) => void;
	logout: () => void;
};

export const AuthContext = createContext<AuthContextValue>({
	user: null,
	login: () => {},
	logout: () => {},
});

export function AuthProvider({ children }: React.PropsWithChildren) {
	const [user, setUser] = useState<User | null>(null);

	const value: AuthContextValue = {
		user,

		login: (user) => {
			setUser(user);
		},

		logout: () => {
			setUser(null);
		},
	};

	return (
		<AuthContext.Provider value={value}>{children}</AuthContext.Provider>
	);
}

export function useAuth() {
	return useContext(AuthContext);
}

Normally, the application might use it like this:

<AuthProvider>
	<App />
</AuthProvider>

With react-context-manager, register the existing context and its existing provider.

Register the existing context

// contextRegistry.ts

import { defineContext, registerContext } from "react-context-manager";

import { AuthContext, AuthProvider } from "./AuthContext";

registerContext("AuthContext", AuthContext);

defineContext({
	name: "AuthContext",
	provider: AuthProvider,
});

The two registrations have different purposes:

  • registerContext() registers the existing React Context with the manager.
  • defineContext() registers the provider that should be composed by ContextRoot.

This allows an existing context implementation to be integrated without rewriting it.

Use ContextRoot

// main.tsx

import React from "react";
import { createRoot } from "react-dom/client";

import { ContextRoot } from "react-context-manager";

import "./contextRegistry";
import App from "./App";

createRoot(document.getElementById("root")!).render(
	<ContextRoot>
		<App />
	</ContextRoot>,
);

ContextRoot now renders the registered AuthProvider around the application.

Conceptually, the result is still:

<AuthProvider>
	<App />
</AuthProvider>

but the provider does not need to be manually nested in your application root.

Consuming the existing context

You can continue using the application's existing hook:

import { useAuth } from "./AuthContext";

export function Profile() {
	const { user, logout } = useAuth();

	return (
		<div>
			<p>{user?.name}</p>

			<button onClick={logout}>Logout</button>
		</div>
	);
}

You can also access the registered context by name:

import { useNamedContext } from "react-context-manager";

type AuthContextValue = {
	user: {
		id: string;
		name: string;
	} | null;
	login: (user: { id: string; name: string }) => void;
	logout: () => void;
};

export function Profile() {
	const { user, logout } = useNamedContext<AuthContextValue>("AuthContext");

	return (
		<div>
			<p>{user?.name}</p>

			<button onClick={logout}>Logout</button>
		</div>
	);
}

When to use this approach

Use this approach when:

  • your project already has React Contexts
  • providers already exist
  • you want to introduce automatic provider composition
  • you don't want to rewrite existing context implementations

The existing Context and Provider can remain in the application while react-context-manager handles their registration and composition.


Example 2 — Creating Contexts Using Only react-context-manager

You can also build your contexts entirely through the package.

This approach uses defineProvider().

Let's create an authentication context and a user context.

Define the authentication provider

// contexts.ts

import { useState } from "react";

import { defineProvider } from "react-context-manager";

type User = {
	id: string;
	name: string;
};

type AuthValue = {
	user: User | null;
	login: (user: User) => void;
	logout: () => void;
};

export const Auth = defineProvider<AuthValue>({
	name: "AuthContext",

	defaultValue: {
		user: null,
		login: () => {},
		logout: () => {},
	},

	useValue: () => {
		const [user, setUser] = useState<User | null>(null);

		return {
			user,

			login: (user: User) => {
				setUser(user);
			},

			logout: () => {
				setUser(null);
			},
		};
	},
});

defineProvider() creates and registers:

  • the React Context
  • the Provider
  • the context definition
  • a useContext() helper

The returned registration contains:

Auth.name;
Auth.context;
Auth.provider;
Auth.useContext;

Define a dependent provider

Now create a user context that depends on the authentication context.

type UserValue = {
	userId: string | null;
};

export const User = defineProvider<UserValue>({
	name: "UserContext",

	defaultValue: {
		userId: null,
	},

	dependsOn: ["AuthContext"],

	useValue: () => {
		const auth = Auth.useContext();

		return {
			userId: auth.user?.id ?? null,
		};
	},
});

The important part is:

dependsOn: ["AuthContext"];

This tells the manager that AuthContext must be composed before UserContext.

You don't need to manually write:

<Auth.provider>
	<User.provider>
		<App />
	</User.provider>
</Auth.provider>

The manager handles that ordering.


Use ContextRoot

Registering the providers is enough.

// main.tsx

import React from "react";
import { createRoot } from "react-dom/client";

import { ContextRoot } from "react-context-manager";

import "./contexts";
import App from "./App";

createRoot(document.getElementById("root")!).render(
	<ContextRoot>
		<App />
	</ContextRoot>,
);

The package automatically composes the providers based on their dependency graph.

Conceptually:

<Auth.provider>
	<User.provider>
		<App />
	</User.provider>
</Auth.provider>

Consume the context

You can use the useContext function returned from defineProvider():

import { Auth, User } from "./contexts";

export function Profile() {
	const auth = Auth.useContext();
	const user = User.useContext();

	return (
		<div>
			<h2>{auth.user?.name ?? "Guest"}</h2>

			<p>User ID: {user.userId ?? "Not logged in"}</p>

			{auth.user && <button onClick={auth.logout}>Logout</button>}
		</div>
	);
}

Alternatively, use the named API:

import { useNamedContext } from "react-context-manager";

export function Profile() {
	const auth = useNamedContext<AuthValue>("AuthContext");

	const user = useNamedContext<UserValue>("UserContext");

	return (
		<div>
			<h2>{auth.user?.name ?? "Guest"}</h2>

			<p>User ID: {user.userId ?? "Not logged in"}</p>
		</div>
	);
}

When to use this approach

Use defineProvider() when:

  • you are starting a new application
  • you want the package to create the Contexts for you
  • you want providers to be registered automatically
  • you want dependency relationships between contexts
  • you want to access contexts through named APIs

Provider Ordering

Provider ordering is one of the main features of the package.

There are two ways to control ordering:

  1. Dependencies
  2. Priority

Dependencies

Use dependsOn when one provider depends on another.

defineContext({
	name: "UserContext",
	provider: UserProvider,
	dependsOn: ["AuthContext"],
});

defineContext({
	name: "AuthContext",
	provider: AuthProvider,
});

The resulting order is:

AuthProvider
    ↓
UserProvider
    ↓
Application

Even though UserContext was registered first, its dependency causes AuthContext to be placed before it.


Priority

For independent contexts, use priority.

defineContext({
	name: "LowPriority",
	provider: LowPriorityProvider,
	priority: 10,
});

defineContext({
	name: "HighPriority",
	provider: HighPriorityProvider,
	priority: 1,
});

Lower priority values are composed first:

HighPriorityProvider
        ↓
LowPriorityProvider
        ↓
Application

Priority is useful when providers do not depend directly on each other but still need deterministic ordering.


Combining Dependencies and Priority

You can use both:

defineContext({
	name: "UserContext",
	provider: UserProvider,
	priority: 10,
	dependsOn: ["AuthContext"],
});

defineContext({
	name: "AuthContext",
	provider: AuthProvider,
	priority: 1,
});

Dependencies determine required ordering relationships, while priority provides ordering for contexts that are otherwise independent.


API Reference

ContextRoot

Automatically composes all registered providers around its children.

<ContextRoot>
	<App />
</ContextRoot>

defineContext()

Registers an existing provider.

defineContext({
	name: "AuthContext",
	provider: AuthProvider,
});

Options

defineContext({
	name: "UserContext",
	provider: UserProvider,
	priority: 10,
	dependsOn: ["AuthContext"],
});

| Property | Description | | ----------- | --------------------------------------------------- | | name | Unique name of the context | | provider | React provider component | | priority | Optional ordering priority | | dependsOn | Optional list of context names that must come first |


defineProvider()

Creates a React Context and Provider and registers them with the manager.

const Auth = defineProvider({
	name: "AuthContext",
	defaultValue: null,
	useValue: () => null,
});

Options

defineProvider({
	name: "AuthContext",
	defaultValue: null,
	useValue: () => null,
	displayName: "AuthProvider",
	priority: 1,
	dependsOn: ["ConfigContext"],
});

| Property | Description | | -------------- | ------------------------------------------------------------------- | | name | Unique context name | | defaultValue | Default React Context value | | useValue | Function used by the generated Provider to obtain the current value | | displayName | Optional React Provider display name | | priority | Optional ordering priority | | dependsOn | Optional provider dependencies |

The returned registration contains:

{
  name,
  context,
  provider,
  useContext,
}

createContext()

Creates and registers a React Context without creating a provider definition.

const AuthContext = createContext("AuthContext", null);

This is useful when you want the package to manage the Context registry but want to define the provider separately.

const AuthContext = createContext("AuthContext", null);

function AuthProvider({ children }: React.PropsWithChildren) {
	return <AuthContext.Provider value={null}>{children}</AuthContext.Provider>;
}

defineContext({
	name: "AuthContext",
	provider: AuthProvider,
});

registerContext()

Registers an existing React Context.

const AuthContext = React.createContext(null);

registerContext("AuthContext", AuthContext);

This is particularly useful when integrating the package into an existing application.


fetchContext()

Retrieves a registered React Context by name.

const AuthContext = fetchContext<AuthValue>("AuthContext");

If the context does not exist, the function throws an error.


useNamedContext()

Reads a registered React Context by name.

const auth = useNamedContext<AuthValue>("AuthContext");

This can be useful when you want to consume contexts without importing individual Context objects.


Choosing the Right API

| Situation | API | | ------------------------------------------------------------ | --------------------------------------- | | Existing Context + existing Provider | registerContext() + defineContext() | | Existing Provider but no need to register Context separately | defineContext() | | Create Context + Provider through the package | defineProvider() | | Create a Context through the package | createContext() | | Retrieve a Context object by name | fetchContext() | | Consume a Context by name | useNamedContext() | | Automatically compose providers | ContextRoot |


Error Handling

The manager validates the provider registry and dependency graph.

For example, if a React Context is created but no provider is registered:

createContext("AuthContext", null);

and the application renders:

<ContextRoot>
	<App />
</ContextRoot>

the manager reports the missing provider registration.

Missing dependencies are also detected:

defineContext({
	name: "UserContext",
	provider: UserProvider,
	dependsOn: ["AuthContext"],
});

If AuthContext has not been registered, an error is raised.

Circular dependencies are also detected:

ContextA → ContextB
ContextB → ContextA

This prevents an invalid provider composition from being rendered.


Dynamic Registration

Providers can be registered after ContextRoot has already mounted.

function App() {
	return (
		<ContextRoot>
			<Application />
		</ContextRoot>
	);
}

Later, another provider can be registered:

defineProvider({
	name: "DynamicContext",
	defaultValue: null,
	useValue: () => null,
});

When the application renders again, ContextRoot can incorporate the updated registry.


TypeScript

The package is written in TypeScript and provides type declarations.

For example:

type AuthValue = {
	user: string | null;
};

const Auth = defineProvider<AuthValue>({
	name: "AuthContext",

	defaultValue: {
		user: null,
	},

	useValue: () => ({
		user: "Arush",
	}),
});

Context consumers remain type-safe:

const auth = Auth.useContext();

auth.user;

You can also provide the type when using named access:

const auth = useNamedContext<AuthValue>("AuthContext");

Recommended Project Structure

A project can keep context definitions in a dedicated directory:

src/
├── contexts/
│   ├── auth.ts
│   ├── user.ts
│   └── index.ts
│
├── components/
│   └── App.tsx
│
├── App.tsx
└── main.tsx

For example:

// contexts/auth.ts

export const Auth = defineProvider<AuthValue>({
	name: "AuthContext",
	defaultValue: {
		user: null,
	},
	useValue: useAuthValue,
});

Then import the context definitions before rendering ContextRoot:

import "./contexts";

createRoot(root).render(
	<ContextRoot>
		<App />
	</ContextRoot>,
);

How Provider Composition Works

The library maintains a registry of:

  • React Contexts
  • Provider definitions
  • Dependencies
  • Priorities

When ContextRoot renders, the registered definitions are resolved into an ordered provider chain.

For example:

defineContext({
	name: "ConfigContext",
	provider: ConfigProvider,
	priority: 1,
});

defineContext({
	name: "AuthContext",
	provider: AuthProvider,
	priority: 2,
});

defineContext({
	name: "UserContext",
	provider: UserProvider,
	dependsOn: ["AuthContext"],
});

The resulting structure is conceptually:

ConfigProvider
      ↓
AuthProvider
      ↓
UserProvider
      ↓
Application

The application only needs:

<ContextRoot>
	<App />
</ContextRoot>

React Compatibility

This package declares React as a peer dependency and supports:

  • React 18
  • React 19

Install React separately in your application.


License

MIT License

Copyright (c) 2026 Arush Shrivastava

See LICENSE.md for the full license text.


Author

Arush Shrivastava

GitHub: https://github.com/arush-shri