diff --git a/.changeset/strong-moose-attend.md b/.changeset/strong-moose-attend.md
new file mode 100644
index 000000000..b8b78b251
--- /dev/null
+++ b/.changeset/strong-moose-attend.md
@@ -0,0 +1,5 @@
+---
+"@solid-primitives/context": minor
+---
+
+Add `ContextConsumer`
diff --git a/packages/context/README.md b/packages/context/README.md
index fd117dca6..61bf903dc 100644
--- a/packages/context/README.md
+++ b/packages/context/README.md
@@ -16,6 +16,7 @@ Primitives simplifying the creation and use of SolidJS Context API.
- [`createOptionalContextProvider`](#createoptionalcontextprovider) - Like `createContextProvider`, but returns `undefined` instead of throwing if the context is missing.
- [`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.
+- [`ContextConsumer`](#contextconsumer) - A component that allows you to consume contexts directly within JSX.
## Installation
@@ -186,6 +187,72 @@ import { MultiProvider } from "@solid-primitives/context";
> **Warning**
> Components and values passed to `MultiProvider` will be evaluated only once, so make sure that the structure is static. If it isn't, please use nested provider components instead.
+## `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 by `createContextProvider` or an inline function that returns the context's value: `() => useContext(MyContext)`.
+* A raw SolidJS context returned by `createContext()`.
+
+In case the context's value is `undefined` (e.g. by using `createOptionalContextProvider`), a fallback element specified in the `fallback` prop is rendered instead. If the context is not provided at all, the component will throw an error.
+
+```tsx
+import { createContextProvider, ContextConsumer } from "@solid-primitives/context";
+
+// Create a context provider
+const [CounterProvider, useCounter] = createContextProvider(() => {
+ const [count, setCount] = createSignal(0);
+ const increment = () => setCount(count() + 1);
+ return { count, increment };
+});
+
+// Provide and consume the context within same JSX block
+return (
+
+ (
+
+
Fallback
+
+ )}>
+ {({ count, increment }) => (
+
+
Count: {count()}
+
+
+ )}
+
+
+);
+```
+
+With the raw SolidJS context returned by `createContext()`:
+
+```tsx
+import { ContextConsumer } from "@solid-primitives/context";
+
+// Create a context
+const CounterContext = createContext(/*...*/);
+
+// Consume it using the raw context
+
+
+ {({ count, increment }) => {
+
+
Count: {count()}
+
+
+ }}
+
+
+```
+
## Changelog
See [CHANGELOG.md](./CHANGELOG.md)
diff --git a/packages/context/src/index.ts b/packages/context/src/index.ts
index 39a4dbf57..6d5bb0bff 100644
--- a/packages/context/src/index.ts
+++ b/packages/context/src/index.ts
@@ -194,11 +194,11 @@ Type validation of the `values` array thanks to the amazing @otonashixav (https:
export function MultiProvider(props: {
values: {
[K in keyof T]:
- | readonly [
- Context | ContextProviderComponent,
- [T[K]][T extends unknown ? 0 : never],
- ]
- | FlowComponent;
+ | readonly [
+ Context | ContextProviderComponent,
+ [T[K]][T extends unknown ? 0 : never],
+ ]
+ | FlowComponent;
};
children: Element;
}): Element {
@@ -222,3 +222,97 @@ export function MultiProvider(props
};
return fn(0);
}
+
+/**
+ * A component that allows you to consume a context without extracting the children into a separate function.
+ * This is particularly useful when the context needs to be used within the same JSX where it is provided.
+ *
+ * The `ContextConsumer` component is equivalent to the following code and solely exists as syntactic sugar:
+ *
+ * ```tsx
+ *
+ * {untrack(() => {
+ * const context = useContext(counterContext); // or useCounter()
+ * return children(context);
+ * })}
+ *
+ * ```
+ *
+ * @param provider Either one of the following:
+ * - A function that returns the context value. Preferably the `use...()` function returned by `createContextProvider()`.
+ * - The context itself returned by `createContext()`.
+ * - An inline function that returns the context's value.
+ *
+ * Please note that this prop is not reactive.
+ *
+ * @param fallback Optional fallback element to render when the context's value is `undefined`. Can be a function or an element. Note that the component still throws an error if the context is not provided, regardless of the fallback.
+ *
+ * @example
+ * with `createContextProvider`:
+ * ```tsx
+ * // create the context
+ * const [CounterProvider, useCounter] = createContextProvider(...)
+ *
+ * // provide and use the context
+ *
+ *
+ * {({ count }) => (
+ *
Count: {count()}
+ * )}
+ *
+ *
+ * ```
+ *
+ * @example
+ * with `createOptionalContextProvider` (and `fallback`):
+ * ```tsx
+ * // create the optional context
+ * const [OptionalCounterProvider, useOptionalCounter] = createOptionalContextProvider(...)
+ *
+ * // provide and use the context
+ *
+ * (
+ *
No counter provided
+ * )}>
+ * {({ count }) => (
+ *
Count: {count()}
+ * )}
+ *
+ *
+ * ```
+ *
+ * @example
+ * with `createContext`:
+ * ```tsx
+ * // create the context
+ * const CounterContext = createContext(...);
+ *
+ * // provide and use the context
+ *
+ *
+ * {({ count }) => (
+ *