Skip to main content

Overview

createCustomContext is a utility function that creates a type-safe React context with enhanced features like strict mode validation, custom error messages, and value extension capabilities.

Import

import { createCustomContext } from "@zayne-labs/toolkit-react";

Signature

function createCustomContext<TContextValue = null, TStrict extends boolean = true>(
  options?: CustomContextOptions<TContextValue, TStrict>
): [
  Provider: React.Context<TContextValue>,
  useCustomContext: UseCustomContext<TContextValue, TStrict>
]

Parameters

options
CustomContextOptions<TContextValue, TStrict>
default:"{}"
Configuration options for the custom context
options.defaultValue
TContextValue | null
default:"null"
The default value for the context
options.errorMessage
string
Custom error message when context is used outside of provider in strict mode
options.extendValue
(contextValue: TContextValue | null) => TContextValue | null
Function to extend or transform the context value before returning it
options.hookName
string
default:"'useUnnamedContext'"
Name of the hook for error messages
options.name
string
Display name for the context (defaults to provider name without ‘Provider’ suffix)
options.providerName
string
default:"'UnnamedContextProvider'"
Name of the provider for error messages
options.strict
boolean
default:"true"
When true, throws an error if context is used outside of provider

Return Value

[0]
React.Context<TContextValue>
The React context Provider component
[1]
UseCustomContext<TContextValue, TStrict>
Hook to access the context value
  • Returns TContextValue when strict is true
  • Returns TContextValue | null when strict is false

Types

type CustomContextOptions<TContextValue, TStrict extends boolean> = {
  defaultValue?: TContextValue | null;
  errorMessage?: string;
  extendValue?: (contextValue: NoInfer<TContextValue> | null) => TContextValue | null;
  hookName?: string;
  name?: string;
  providerName?: string;
  strict?: TStrict;
};

type UseCustomContext<TContextValue, TStrict extends boolean> = () => TStrict extends true
  ? TContextValue
  : TContextValue | null;

Examples

Basic Usage

import { createCustomContext } from "@zayne-labs/toolkit-react";

type ThemeContextValue = {
  theme: "light" | "dark";
  toggleTheme: () => void;
};

const [ThemeProvider, useTheme] = createCustomContext<ThemeContextValue>({
  name: "Theme",
  hookName: "useTheme",
  providerName: "ThemeProvider",
});

function App() {
  const [theme, setTheme] = useState<"light" | "dark">("light");
  
  const value = {
    theme,
    toggleTheme: () => setTheme(prev => prev === "light" ? "dark" : "light"),
  };
  
  return (
    <ThemeProvider.Provider value={value}>
      <Content />
    </ThemeProvider.Provider>
  );
}

function Content() {
  const { theme, toggleTheme } = useTheme();
  
  return (
    <div>
      <p>Current theme: {theme}</p>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
}

Non-Strict Mode

const [UserProvider, useUser] = createCustomContext<User, false>({
  name: "User",
  strict: false, // Allow usage outside of provider
});

function Component() {
  const user = useUser(); // user can be User | null
  
  if (!user) {
    return <div>Not logged in</div>;
  }
  
  return <div>Welcome, {user.name}</div>;
}

With Custom Error Message

const [AuthProvider, useAuth] = createCustomContext<AuthContextValue>({
  name: "Auth",
  hookName: "useAuth",
  providerName: "AuthProvider",
  errorMessage: "useAuth must be used within an AuthProvider. Make sure your component is wrapped with <AuthProvider>.",
});

With Value Extension

const [SettingsProvider, useSettings] = createCustomContext<Settings>({
  name: "Settings",
  extendValue: (value) => {
    // Add computed properties or merge with defaults
    if (!value) return null;
    
    return {
      ...value,
      isProduction: value.environment === "production",
    };
  },
});

Error Handling

When strict mode is enabled (default), the hook throws a ContextError if used outside of the provider:
class ContextError extends Error {
  name = "ContextError";
}
The default error message format is:
{hookName} returned "null". Did you forget to wrap the necessary components within {providerName}?

Best Practices

  1. Always provide meaningful names: Use the name, hookName, and providerName options for better debugging
  2. Use strict mode by default: Only disable strict mode when you have a valid use case for optional context
  3. Provide custom error messages: Help developers understand where to wrap components with the provider
  4. Type your context value: Always provide a type parameter for better type safety

Build docs developers (and LLMs) love