An ergonomic superset of React's Context API for butter smooth dependency injection π§
This library is meant to be used with the React Compiler. Relying heavily on the Context API without the React Compiler can cause serious performance sinks in your application.
superctx extends React's Context API with:
- β Better TypeScript inference than native Context API
- β Automatic error handling for missing providers
- β
Enhanced base context providers with
addBase - β Easy access to multiple contexts
- β Lazy initialization of default values
- β Zero dependencies (only uses React)
Especially recommended for AI coding agents:
- β Less boilerplate means lower token consumption
- β More compact imports keep prompts and diffs shorter
- β Lower token usage leads to better iteration quality
- React 19 or above
pnpm add superctximport { createSuperContext } from "superctx";
// Context that requires a provider
const UserContext = createSuperContext<{ name: string; email: string }>({
notProvidedMessage: "User context not provided",
});
// Context with default value
const ThemeContext = createSuperContext<"light" | "dark">({
initialValue: "light",
});
// Context with lazy initialization
const ConfigContext = createSuperContext<AppConfig>({
getInitialValue: () => loadConfigFromStorage(),
});function UserProfile() {
const user = UserContext.useProvided();
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
// Consumer pattern (alternative to hook)
function UserDisplay() {
return <UserContext.Consumer>{(user) => <div>{user.name}</div>}</UserContext.Consumer>;
}Create root components that automatically provide context values:
import { createSuperContext, addBase } from "superctx";
const Environment = addBase(
createSuperContext<NebulyEnvironment>({
notProvidedMessage: "Environment not provided",
}),
(Provider) => {
return ({ children }) => {
const env = getEnvironmentValues();
return <Provider value={env}>{children}</Provider>;
};
},
);
// Usage - just use the Base component
function App() {
return (
<Environment.Base>
<YourApp />
</Environment.Base>
);
}
// Access the context anywhere
function Component() {
const env = Environment.useProvided();
return <div>{env.apiUrl}</div>;
}Use useProviders to access multiple contexts at once:
import { useProviders } from "superctx";
function MyComponent() {
const [user, theme, environment] = useProviders([UserContext, ThemeContext, Environment]);
return (
<div className={theme}>
<h1>{user.name}</h1>
<p>API: {environment.apiUrl}</p>
</div>
);
}Use the Consumer component for cleaner conditional rendering with multiple contexts:
import { Consumer } from "superctx";
function DataDisplay() {
return (
<Consumer providers={[UserContext, ProjectContext]}>
{([user, project]) => (
<div>
<h1>{user.name}</h1>
<h2>{project.name}</h2>
</div>
)}
</Consumer>
);
}import { createSuperContext, addBase } from "superctx";
type NebulyEnvironment = {
apiUrl: string;
environment: "development" | "production";
flags?: Record<string, boolean>;
};
export const Environment = addBase(
createSuperContext<NebulyEnvironment>({
notProvidedMessage: "Environment not provided",
}),
(Provider) => {
return ({ children }) => {
const env = getEnvironmentValues();
return <Provider value={env}>{children}</Provider>;
};
},
);
// Usage
function App() {
return (
<Environment.Base>
<Router />
</Environment.Base>
);
}
function ApiClient() {
const { apiUrl } = Environment.useProvided();
// Use apiUrl to configure API client
}import { createSuperContext, addBase } from "superctx";
export const Theme = addBase(
createSuperContext<"light" | "dark" | "system">({
initialValue: "system",
}),
(Provider) => {
return ({ children, defaultTheme = "system" }) => {
const [theme, setTheme] = useState(defaultTheme);
return <Provider value={theme}>{children}</Provider>;
};
},
);
// Usage
function App() {
return (
<Theme.Base defaultTheme="light">
<YourApp />
</Theme.Base>
);
}import { useProviders, Consumer } from "superctx";
// Access multiple contexts
function Dashboard() {
const [user, project, environment] = useProviders([UserContext, ProjectContext, Environment]);
return (
<div>
<h1>Welcome, {user.name}</h1>
<p>Project: {project.name}</p>
<p>Environment: {environment.environment}</p>
</div>
);
}
// Or use Consumer component for conditional rendering
function ConditionalContent() {
return (
<Consumer providers={[UserContext, FeatureFlags]}>
{([user, flags]) => {
if (flags.isAdmin && user.role === "admin") {
return <AdminPanel />;
}
return <RegularContent />;
}}
</Consumer>
);
}import { createSuperContext, addBase } from "superctx";
import { DateFormatter, NumberFormatter, TimeFormatter } from "formatters";
export const Formatters = addBase(
createSuperContext<{
numberFmt: NumberFormatter;
timeFmt: TimeFormatter;
dateFmt: DateFormatter;
}>({
notProvidedMessage: "Formatters are not provided",
}),
(Provider) => {
return ({ children }) => {
const { language } = Localization.useProvided();
return (
<Provider
value={{
numberFmt: new NumberFormatter({ locale: language }),
timeFmt: new TimeFormatter({ locale: language }),
dateFmt: new DateFormatter({ locale: language }),
}}
>
{children}
</Provider>
);
};
},
);
// Usage
function PriceDisplay({ amount }: { amount: number }) {
const { numberFmt } = Formatters.useProvided();
return <span>{numberFmt.compactFloat(amount)}</span>;
}Creates a super context with enhanced features.
Options:
initialValue: T: Default value (context is always provided)getInitialValue: () => T: Lazy initialization functionnotProvidedMessage: string: Error message if provider is missing
Returns:
Provider: React context provider componentConsumer: React context consumer componentuseProvided(): Hook to access context value
Creates a base component for a context.
Parameters:
context: A super contextbaseFactory: Function that takes Provider and returns a base component
Returns: Context with added Base component
Hook to access multiple contexts at once.
Parameters:
deps: Array of super contexts
Returns: Tuple of context values (typed)
Component for conditional rendering with multiple contexts.
Props:
providers: Array of super contextschildren: Render function receiving context values
Error class thrown when a required context is not provided.
When a required context is missing, superctx throws a MissingProviderError as soon as you try to read it.
This fail-fast behavior is intentional: it surfaces provider mistakes immediately, instead of silently propagating null values and causing harder-to-debug errors later.
import { createContext, useContext } from "react";
export const MyContext = createContext<{
foo: "bar";
}>(null!);
export function useMyContext() {
const value = useContext(MyContext);
if (!value) {
throw new Error("useMyContext requires a MyContext provider");
}
return value;
}
export function SomeComponent() {
const { foo } = useMyContext();
return <p>{foo}</p>;
}import { createSuperContext } from "superctx";
export const MyContext = createSuperContext<{
foo: "bar";
}>({
notProvidedMessage: "MyContext provider is missing",
});
export function SomeComponent() {
const { foo } = MyContext.useProvided();
return <p>{foo}</p>;
}