(
factoryFn: (props: P) => T,
defaults?: T,
): [provider: ContextProvider, useContext: () => T | undefined] {
- const ctx = createContext(defaults);
+ const ctx = createContext(defaults);
return [
props => {
- return createComponent(ctx.Provider, {
+ return createComponent(ctx, {
value: factoryFn(props),
get children() {
return props.children;
@@ -79,16 +79,16 @@ Type validation of the `values` array thanks to the amazing @otonashixav (https:
* @example
* ```tsx
* // before
- *
- *
+ *
+ *
*
- *
- *
+ *
+ *
*
* // after
*
*
*
@@ -103,15 +103,15 @@ export function MultiProvider(props
]
| FlowComponent;
};
- children: JSX.Element;
-}): JSX.Element {
+ children: Element;
+}): Element {
const { values } = props;
const fn = (i: number) => {
let item: any = values[i];
if (!item) return props.children;
- const ctxProps: { value?: any; children: JSX.Element } = {
+ const ctxProps: { value?: any; children: Element } = {
get children() {
return fn(i + 1);
},
@@ -119,7 +119,6 @@ export function MultiProvider(props
if (Array.isArray(item)) {
ctxProps.value = item[1];
item = item[0];
- if (typeof item !== "function") item = item.Provider;
}
return createComponent(item, ctxProps);
diff --git a/packages/context/test/index.test.tsx b/packages/context/test/index.test.tsx
index a60a16639..51dfaa40c 100644
--- a/packages/context/test/index.test.tsx
+++ b/packages/context/test/index.test.tsx
@@ -1,25 +1,24 @@
import { describe, test, expect } from "vitest";
import {
createContext,
- createRoot,
type FlowComponent,
- type JSX,
+ type Element,
untrack,
useContext,
} from "solid-js";
-import { render } from "solid-js/web";
+import { render } from "@solidjs/web";
import { createContextProvider, MultiProvider } from "../src/index.js";
type TestContextValue = {
message: string;
- children: JSX.Element;
+ children: Element;
};
const TEST_MESSAGE = "Hello, Context!";
const FALLBACK: TestContextValue = { message: "FALLBACK", children: undefined };
const [TestProvider, useTestContext] = createContextProvider(
- (props: { text?: string; children: JSX.Element }): TestContextValue => {
+ (props: { text?: string; children: Element }): TestContextValue => {
return {
message: props.text ?? TEST_MESSAGE,
get children() {
@@ -94,23 +93,29 @@ describe("MultiProvider", () => {
return {props.children};
};
- createRoot(() => {
-
- {untrack(() => {
- runs++;
- capture1 = useContext(Ctx1);
- capture2 = useContext(Ctx2);
- capture3 = useTestContext().message;
- return "";
- })}
- ;
- });
+ const container = document.createElement("div");
+ const unmount = render(
+ () => (
+
+ {untrack(() => {
+ runs++;
+ capture1 = useContext(Ctx1);
+ capture2 = useContext(Ctx2);
+ capture3 = useTestContext().message;
+ return "";
+ })}
+
+ ),
+ container,
+ );
expect(runs).toBe(1);
expect(capture1).toBe("Hello");
expect(capture2).toBe("World");
expect(capture3).toBe(TEST_MESSAGE);
+
+ unmount();
});
});
diff --git a/packages/context/test/server.test.tsx b/packages/context/test/server.test.tsx
index 8db05a5eb..b782e71b0 100644
--- a/packages/context/test/server.test.tsx
+++ b/packages/context/test/server.test.tsx
@@ -1,18 +1,18 @@
import { describe, test, expect } from "vitest";
-import { createContext, type FlowComponent, type JSX, untrack, useContext } from "solid-js";
-import { renderToString } from "solid-js/web";
+import { createContext, type FlowComponent, type Element, untrack, useContext } from "solid-js";
+import { renderToString } from "@solidjs/web";
import { createContextProvider, MultiProvider } from "../src/index.js";
type TestContextValue = {
message: string;
- children: JSX.Element;
+ children: Element;
};
const TEST_MESSAGE = "Hello, Context!";
const FALLBACK: TestContextValue = { message: "FALLBACK", children: undefined };
const [TestProvider, useTestContext] = createContextProvider(
- (props: { children: JSX.Element }): TestContextValue => {
+ (props: { children: Element }): TestContextValue => {
return {
message: TEST_MESSAGE,
get children() {
@@ -41,7 +41,7 @@ describe("MultiProvider", () => {
renderToString(() => (
{untrack(() => {
runs++;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b7a083eac..ef360a457 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -197,9 +197,12 @@ importers:
packages/context:
devDependencies:
+ '@solidjs/web':
+ specifier: 2.0.0-beta.13
+ version: 2.0.0-beta.13(solid-js@2.0.0-beta.13)
solid-js:
- specifier: ^1.9.7
- version: 1.9.7
+ specifier: 2.0.0-beta.13
+ version: 2.0.0-beta.13
packages/controlled-props:
dependencies:
From 943027f76e99fd33fffd5c7b84560fd8a4e3b6cf Mon Sep 17 00:00:00 2001
From: David Di Biase <1168397+davedbase@users.noreply.github.com>
Date: Thu, 21 May 2026 10:40:19 -0400
Subject: [PATCH 2/2] Imprved context options, added new strict and layered
primitive
---
packages/context/CHANGELOG.md | 6 +
packages/context/README.md | 64 ++++++++++-
packages/context/package.json | 2 +
packages/context/src/index.ts | 106 ++++++++++++++++-
packages/context/test/index.test.tsx | 157 +++++++++++++++++++++++++-
packages/context/test/server.test.tsx | 52 ++++++++-
6 files changed, 379 insertions(+), 8 deletions(-)
diff --git a/packages/context/CHANGELOG.md b/packages/context/CHANGELOG.md
index 681999f78..4ed19184e 100644
--- a/packages/context/CHANGELOG.md
+++ b/packages/context/CHANGELOG.md
@@ -14,6 +14,12 @@
- `JSX.Element` replaced with `Element` from `solid-js` throughout the public API types.
- `MultiProvider` no longer falls back to accessing `.Provider` on non-function items — contexts passed in `values` must be functions (which all `Context` objects are in Solid 2.0).
+#### New Exports
+
+- **`createStrictContextProvider`** — Like `createContextProvider` without defaults, but with the intent made explicit in types: the hook returns `T` (never `undefined`) and Solid throws `ContextNotFoundError` at runtime when used outside a provider. Accepts an optional `{ name }` option.
+- **`createLayeredContext`** — Like `createContextProvider`, but each provider in the tree extends the parent context value rather than replacing it. The factory receives both `props` and the nearest parent context value. Requires a `defaults` base value.
+- **`ContextProviderOptions`** — Exported type `{ name?: string }` for the third argument of `createContextProvider` and `createStrictContextProvider`/`createLayeredContext`'s second argument.
+
## 0.3.2
### Patch Changes
diff --git a/packages/context/README.md b/packages/context/README.md
index 7b21982ab..e5edcf396 100644
--- a/packages/context/README.md
+++ b/packages/context/README.md
@@ -11,6 +11,8 @@
Primitives simplifying the creation and use of SolidJS Context API.
- [`createContextProvider`](#createcontextprovider) - Create the Context Provider component and useContext function with types inferred from the factory function.
+- [`createStrictContextProvider`](#createstrictcontextprovider) - Like `createContextProvider`, but throws when the context is used outside a provider instead of returning `undefined`.
+- [`createLayeredContext`](#createlayeredcontext) - Like `createContextProvider`, but each provider extends the parent context value rather than replacing it.
- [`MultiProvider`](#multiprovider) - A component that allows you to provide multiple contexts at once.
## Installation
@@ -74,16 +76,72 @@ const [CounterProvider, useCounter] = createContextProvider(
const { count } = useCounter();
```
-Definite context types without defaults:
+### 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).
```ts
-const useDefiniteCounter = () => useCounter()!;
+const [ThemeProvider, useTheme] = createContextProvider(
+ () => createTheme(),
+ defaultTheme,
+ { name: "Theme" },
+);
```
### Demo
https://codesandbox.io/s/solid-primitives-context-demo-oqyie2?file=/index.tsx
+## `createStrictContextProvider`
+
+Like `createContextProvider` without defaults, but with an explicit contract: if the hook is called outside a provider, Solid throws a `ContextNotFoundError` at runtime. The return type of the hook is `T` (never `undefined`), so no null-check is needed at the call site.
+
+```tsx
+import { createStrictContextProvider } from "@solid-primitives/context";
+
+const [AuthProvider, useAuth] = createStrictContextProvider(
+ () => {
+ const [user, setUser] = createSignal(null);
+ return { user, setUser };
+ },
+ { name: "Auth" },
+);
+
+// No `!` needed — type is T, not T | undefined
+const { user } = useAuth();
+```
+
+Use `createStrictContextProvider` when a context is required by contract (e.g. a form context that must be inside `