Primitives simplifying or extending the Context API
| Stage | Category | Version | Last Updated | Demo |
|---|---|---|---|---|
| 3 | Control Flow | 2.0.0-next.5 (next) | Nov 11, 2026 | Demo → |
npm i @solid-primitives/context@nextPrimitives simplifying the creation and use of SolidJS Context API.
createContextProvider- Create the Context Provider component and useContext function with types inferred from the factory function.createOptionalContextProvider- LikecreateContextProvider, but returnsundefinedinstead of throwing if the context is missing.createLayeredContext- LikecreateContextProvider, but each provider extends the parent context value rather than replacing it.MultiProvider- A component that allows you to provide multiple contexts at once.ContextConsumer- A component that allows you to consume contexts directly within JSX.
Requires solid-js@^2.0.0-beta.13 and @solidjs/web@^2.0.0-beta.13.
createContextProvider
Create the Context Provider component and useContext function with types inferred from the factory function.
How to use it
Given a factory function, createContextProvider creates a SolidJS Context and returns both a Provider component for setting the context, and a useContext helper for getting the context. The factory function gets called when the provider component gets executed; all props of the provider component get passed into the factory function, and what it returns will be available in the contexts for all the underlying components. The types of the provider props and context are inferred from the factory function.
import { createContextProvider } from "@solid-primitives/context";
const [CounterProvider, useCounter] = createContextProvider((props: { initial: number }) => { const [count, setCount] = createSignal(props.initial); const increment = () => setCount(count() + 1); return { count, increment };});
// Provide the context<CounterProvider initial={1}> <App /></CounterProvider>;
// Use the context in a child componentconst ctx = useCounter();ctx; // T: { count: () => number; increment: () => void; }Providing context fallback
The createContextProvider primitive takes a second, optional argument for providing context defaults for when the context wouldn't be provided higher in the component tree.
If no fallback is provided, the useContext function will throw when the context is not provided.
const [CounterProvider, useCounter] = createContextProvider( () => { const [count, setCount] = createSignal(0); const increment = () => setCount(count() + 1); return { count, increment }; }, { count: () => 0, increment: () => {}, },);
// then when using the context:const { count } = useCounter();The useContext function always returns Exclude<T, undefined>. If the factory or fallback value is undefined, Solid treats the value as missing and useContext will throw instead of returning it.
Debug name
An optional name can be passed as part of the third argument. It labels the context's Symbol for Solid DevTools and improves ContextNotFoundError stack traces (dev mode only).
const [ThemeProvider, useTheme] = createContextProvider(() => createTheme(), defaultTheme, { name: "Theme",});createOptionalContextProvider
Like createContextProvider, but the useContext function returns undefined when the context is missing or when the provider value is undefined.
import { createOptionalContextProvider } from "@solid-primitives/context";
const [GroupProvider, useGroup] = createOptionalContextProvider((props: { id: string }) => ({ groupId: props.id,}));
const group = useGroup();group; // T: { groupId: string } | undefinedThe fallback is returned when no provider is present. If a provider is present and its factory returns undefined, the useContext function returns undefined.
An optional name can be passed as the third argument.
createLayeredContext
Like createContextProvider, but each provider in the tree extends the parent context value rather than replacing it entirely. The factory function receives the nearest parent's context value as its second argument.
This is useful for incremental overrides such as themes, permissions layers, or i18n patches where a child provider should inherit what it does not explicitly change.
import { createLayeredContext } from "@solid-primitives/context";
const [ThemeProvider, useTheme] = createLayeredContext( (props: { primary?: string; secondary?: string }, parent) => ({ ...parent, primary: props.primary ?? parent.primary, secondary: props.secondary ?? parent.secondary, }), { primary: "blue", secondary: "gray" }, // base defaults);
// Root: { primary: "red", secondary: "gray" }<ThemeProvider primary="red"> {/* Nested: { primary: "green", secondary: "gray" } — secondary inherited */} <ThemeProvider primary="green"> <App /> </ThemeProvider></ThemeProvider>;createLayeredContext always requires a defaults value (the base used when no parent provider wraps the component). The hook return type is always T (never undefined).
MultiProvider
A component that allows you to provide multiple contexts at once.
It will work exactly like nesting multiple providers as separate components, but it will save you from the nesting.
How to use it
MultiProvider takes a single values array. Contexts and custom provider components are paired with the value to provide.
Providers created by createContextProvider, createOptionalContextProvider or createLayeredContext are paired with their props instead, exactly as if you nested them directly, so providers with any prop shape compose without a wrapper component.
Note Values list is evaluated in order, so the context values will be provided in the same way as if you were nesting the providers.
import { MultiProvider } from "@solid-primitives/context";
// before<FooContext value={"foo"}> <BarContext value={"bar"}> <BazContext value={"baz"}> <CounterProvider initial={1} label="primary"> <MyCustomProviderComponent value={"hello-world"}> <BoundContextProvider> <App /> </BoundContextProvider> </MyCustomProviderComponent> </CounterProvider> </BazContext> </BarContext></FooContext>;
// after<MultiProvider values={[ [FooContext, "foo"], [BarContext, "bar"], [BazContext, "baz"], // providers from createContextProvider (and friends) receive the same props as when nested [CounterProvider, { initial: 1, label: "primary" }], // you can also provide a component, the value will be passed to a `value` prop [MyCustomProviderComponent, "hello-world"], // if you have a provider that doesn't accept a `value` prop, you can just pass a function BoundContextProvider, ]}> <App /></MultiProvider>;Warning Components and values passed to
MultiProviderwill be evaluated only once, so make sure that the structure is static. If it isn't, please use nested provider components instead.
Note Before 2.0, a
[Provider, value]pair for a provider created bycreateContextProviderreached its factory asprops.value. Those providers now receive props, so wrap the value to keep that shape:[Provider, { value }]. Getters in the props object stay reactive.
ContextConsumer
Inspired by React's Context.Consumer component, ContextConsumer allows using contexts directly within JSX without the needing to extract the content JSX into a separate function.
This is particularly useful when you want to use the context in the same JSX block where you're providing it and directly bind the context value to the frontend.
Note that this component solely serves as syntactic sugar and doesn't provide any additional functionality over using untrack together with useContext in JSX.
How to use it
ContextConsumer takes a provider prop that can be either one of the following:
- A
use...()function returned bycreateContextProvideror an inline function that returns the context's value:() => useContext(MyContext). - A raw SolidJS context returned by
createContext().
When an optional context's value is undefined (e.g. a useContext function from createOptionalContextProvider), a fallback element specified in the fallback prop is rendered instead. Without a fallback, nothing is rendered.
Note
fallbackcannot suppress errors for required contexts. If a required context (a rawcreateContext()without a default, or auseContextfunction fromcreateContextProviderwithout defaults) is not provided, or is provided withundefined,useContextthrows aContextNotFoundErrorbeforefallbackis considered. UsecreateOptionalContextProviderwhen a missing provider should render the fallback.
import { createOptionalContextProvider, ContextConsumer } from "@solid-primitives/context";
// Create an optional context provider (its `useContext` returns `undefined` when missing)const [CounterProvider, useCounter] = createOptionalContextProvider(() => { const [count, setCount] = createSignal(0); const increment = () => setCount(count() + 1); return { count, increment };});
// Provide and consume the context within same JSX block;// `fallback` renders if this subtree is ever used without `<CounterProvider>`return ( <CounterProvider> <ContextConsumer provider={useCounter} fallback={() => ( <div> <p>Fallback</p> </div> )}> {({ count, increment }) => ( <div> <p>Count: {count()}</p> <button onClick={increment}>Increment</button> </div> )} </ContextConsumer> </CounterProvider>);With the raw SolidJS context returned by createContext():
import { ContextConsumer } from "@solid-primitives/context";
// Create a contextconst CounterContext = createContext(/*...*/);
// Consume it using the raw context<CounterContext> <ContextConsumer provider={CounterContext}> {({ count, increment }) => { <div> <p>Count: {count()}</p> <button onClick={increment}>Increment</button> </div> }} </ContextConsumer></CounterContext>Changelog
See CHANGELOG.md