createContext
Factory for creating a typed React context with a consumer hook that throws a named error when used outside its provider.
Source Code
/**
* Ported from Radix UI (MIT)
* https://github.com/radix-ui/primitives/blob/main/packages/react/context/src/createContext.tsx
*
* Adapted for Playstack:
* - React 19 idioms throughout (per CLAUDE.md).
* - Returns the Context object itself, so consumers render it directly in JSX.
* - Named exports only.
* - Sentinel disambiguates the unmounted-tree case from a tree that passes an
* intentionally undefined value.
*/
import { createContext as reactCreateContext, use } from 'react';
const MISSING_PROVIDER = Symbol('MISSING_PROVIDER');
type WithSentinel<T> = T | typeof MISSING_PROVIDER;
export const createContext = <ContextValue,>(
rootComponentName: string,
defaultValue?: ContextValue
) => {
const Context = reactCreateContext<WithSentinel<ContextValue>>(
defaultValue === undefined ? MISSING_PROVIDER : defaultValue
);
Context.displayName = `${rootComponentName}Context`;
const useContextHook = (): ContextValue => {
const value = use(Context);
if (value === MISSING_PROVIDER) {
throw new Error(
`use${rootComponentName} must be used within a ${rootComponentName} provider`
);
}
return value;
};
// Mirror the variable name in stack traces.
Object.defineProperty(useContextHook, 'name', {
value: `use${rootComponentName}`,
});
return [Context, useContextHook] as const;
}; API Reference
| Prop | Default | Type | Description |
|---|---|---|---|
rootComponentName | - | string | Used to name the Context (.displayName) and to construct the consumer hook error message. |
defaultValue | - | ContextValue | undefined | Optional default value. When provided, the consumer hook returns this value instead of throwing when used outside a provider — supplying any default (including null, false, 0) disables the named-error guard. |
Returns a tuple [Context, useContextHook]. The Context is React’s stock context object — use it directly in JSX (React 19): <Context value={...}>. The consumer hook uses React 19’s use() and throws use${rootComponentName} must be used within a ${rootComponentName} provider when accessed outside.
/**
* Ported from Radix UI (MIT)
* https://github.com/radix-ui/primitives/blob/main/packages/react/context/src/createContext.tsx
*
* Adapted for Playstack:
* - React 19 idioms throughout (per CLAUDE.md).
* - Returns the Context object itself, so consumers render it directly in JSX.
* - Named exports only.
* - Sentinel disambiguates the unmounted-tree case from a tree that passes an
* intentionally undefined value.
*/
import { createContext as reactCreateContext, use } from 'react';
const MISSING_PROVIDER = Symbol('MISSING_PROVIDER');
type WithSentinel<T> = T | typeof MISSING_PROVIDER;
export const createContext = <ContextValue,>(
rootComponentName: string,
defaultValue?: ContextValue
) => {
const Context = reactCreateContext<WithSentinel<ContextValue>>(
defaultValue === undefined ? MISSING_PROVIDER : defaultValue
);
Context.displayName = `${rootComponentName}Context`;
const useContextHook = (): ContextValue => {
const value = use(Context);
if (value === MISSING_PROVIDER) {
throw new Error(
`use${rootComponentName} must be used within a ${rootComponentName} provider`
);
}
return value;
};
// Mirror the variable name in stack traces.
Object.defineProperty(useContextHook, 'name', {
value: `use${rootComponentName}`,
});
return [Context, useContextHook] as const;
}; Examples
Replacing the hand-rolled pattern
Before:
import { createContext as reactCreateContext, use } from 'react';
interface DisclosureContextValue {
open: boolean;
setOpen: (open: boolean) => void;
}
const DisclosureContext = reactCreateContext<DisclosureContextValue | null>(null);
const useDisclosureContext = () => {
const ctx = use(DisclosureContext);
if (!ctx) {
throw new Error('useDisclosureContext must be used within a Disclosure');
}
return ctx;
};
After:
import { createContext } from '@/foundations/utils/create-context/create-context';
interface DisclosureContextValue {
open: boolean;
setOpen: (open: boolean) => void;
}
const [DisclosureContext, useDisclosureContext] =
createContext<DisclosureContextValue>('Disclosure');
// In JSX:
// <DisclosureContext value={{ open, setOpen }}>{children}</DisclosureContext>
Previous
Compose Refs
Next
Debounce