Agents (llms.txt)

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