Skip to main content
Creates a Context object for passing data through the component tree without manually passing props at every level.

Signature

function createContext<T>(defaultValue: T): Context<T>

Parameters

defaultValue
T
required
The default value used when a component does not have a matching Provider above it in the tree.

Return Value

Returns a Context<T> object with the following properties:
interface Context<T> {
  Provider: Provider<T>;
  Consumer: Consumer<T>;
  displayName?: string;
}

interface Provider<T> extends FunctionComponent<{
  value: T;
  children?: ComponentChildren;
}> {}

interface Consumer<T> extends FunctionComponent<{
  children: (value: T) => ComponentChildren;
}> {}

Description

createContext creates a Context object that allows you to share values between components without explicitly passing props through every level of the tree. The Context object includes:
  • Provider: Component that provides the context value
  • Consumer: Component that consumes the context value
Components that consume context will re-render when the Provider’s value changes.

Implementation

The function is implemented in src/create-context.js:6:
export function createContext(defaultValue) {
  function Context(props) {
    if (!this.getChildContext) {
      let subs = new Set();
      let ctx = {};
      ctx[Context._id] = this;

      this.getChildContext = () => ctx;

      this.componentWillUnmount = () => {
        subs = NULL;
      };

      this.shouldComponentUpdate = function (_props) {
        if (this.props.value != _props.value) {
          subs.forEach(c => {
            c._bits |= COMPONENT_FORCE;
            enqueueRender(c);
          });
        }
      };

      this.sub = c => {
        subs.add(c);
        let old = c.componentWillUnmount;
        c.componentWillUnmount = () => {
          if (subs) {
            subs.delete(c);
          }
          if (old) old.call(c);
        };
      };
    }

    return props.children;
  }

  Context._id = '__cC' + i++;
  Context._defaultValue = defaultValue;
  Context.Consumer = (props, contextValue) => {
    return props.children(contextValue);
  };
  Context.Provider = Context._contextRef = Context.Consumer.contextType = Context;

  return Context;
}

Usage Examples

Basic Context

import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

// Create context
const ThemeContext = createContext('light');

// Provider component
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

// Consumer with hooks
function Toolbar() {
  const theme = useContext(ThemeContext);
  return <div className={`toolbar-${theme}`}>Toolbar</div>;
}

With Consumer Component

import { createContext } from 'preact';

const UserContext = createContext(null);

function App() {
  const user = { name: 'John Doe', role: 'admin' };
  
  return (
    <UserContext.Provider value={user}>
      <Profile />
    </UserContext.Provider>
  );
}

function Profile() {
  return (
    <UserContext.Consumer>
      {user => (
        <div>
          <h1>{user.name}</h1>
          <p>Role: {user.role}</p>
        </div>
      )}
    </UserContext.Consumer>
  );
}

Complex Context Value

import { createContext } from 'preact';
import { useState } from 'preact/hooks';

const AppContext = createContext({
  theme: 'light',
  user: null,
  setTheme: () => {},
  setUser: () => {}
});

function AppProvider({ children }) {
  const [theme, setTheme] = useState('light');
  const [user, setUser] = useState(null);
  
  const value = {
    theme,
    user,
    setTheme,
    setUser
  };
  
  return (
    <AppContext.Provider value={value}>
      {children}
    </AppContext.Provider>
  );
}

Multiple Contexts

import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

const ThemeContext = createContext('light');
const UserContext = createContext(null);

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <UserContext.Provider value={{ name: 'Alice' }}>
        <Dashboard />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}

function Dashboard() {
  const theme = useContext(ThemeContext);
  const user = useContext(UserContext);
  
  return (
    <div className={`dashboard-${theme}`}>
      Welcome, {user.name}!
    </div>
  );
}

With TypeScript

import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

interface Theme {
  primary: string;
  secondary: string;
  background: string;
}

const defaultTheme: Theme = {
  primary: '#007bff',
  secondary: '#6c757d',
  background: '#ffffff'
};

const ThemeContext = createContext<Theme>(defaultTheme);

function ThemedButton() {
  const theme = useContext(ThemeContext);
  
  return (
    <button style={{ backgroundColor: theme.primary }}>
      Click me
    </button>
  );
}

Class Component Consumer

import { Component, createContext } from 'preact';

const ThemeContext = createContext('light');

class ThemedButton extends Component {
  static contextType = ThemeContext;
  
  render() {
    const theme = this.context;
    return (
      <button className={`button-${theme}`}>
        {this.props.children}
      </button>
    );
  }
}

Dynamic Context Updates

import { createContext } from 'preact';
import { useState, useContext } from 'preact/hooks';

const CountContext = createContext({ count: 0, increment: () => {} });

function CountProvider({ children }) {
  const [count, setCount] = useState(0);
  
  const value = {
    count,
    increment: () => setCount(c => c + 1)
  };
  
  return (
    <CountContext.Provider value={value}>
      {children}
    </CountContext.Provider>
  );
}

function Counter() {
  const { count, increment } = useContext(CountContext);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

Nested Providers

import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <OuterComponent />
      <ThemeContext.Provider value="light">
        <InnerComponent />
      </ThemeContext.Provider>
    </ThemeContext.Provider>
  );
}

function OuterComponent() {
  const theme = useContext(ThemeContext); // "dark"
  return <div className={theme}>Outer</div>;
}

function InnerComponent() {
  const theme = useContext(ThemeContext); // "light"
  return <div className={theme}>Inner</div>;
}

Best Practices

Separate Context Creation

// contexts/ThemeContext.js
import { createContext } from 'preact';

export const ThemeContext = createContext('light');

// App.js
import { ThemeContext } from './contexts/ThemeContext';

function App() {
  return (
    <ThemeContext.Provider value="dark">
      {/* ... */}
    </ThemeContext.Provider>
  );
}

Custom Hook for Context

import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

const AuthContext = createContext(null);

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
}

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  
  return (
    <AuthContext.Provider value={{ user, setUser }}>
      {children}
    </AuthContext.Provider>
  );
}

Avoid Unnecessary Re-renders

import { createContext } from 'preact';
import { useState, useMemo } from 'preact/hooks';

const AppContext = createContext(null);

function AppProvider({ children }) {
  const [state, setState] = useState({ count: 0 });
  
  // Memoize the context value to prevent unnecessary re-renders
  const value = useMemo(() => ({
    state,
    increment: () => setState(s => ({ count: s.count + 1 }))
  }), [state]);
  
  return (
    <AppContext.Provider value={value}>
      {children}
    </AppContext.Provider>
  );
}

Default Value Usage

The default value is used when no Provider is found:
const ThemeContext = createContext('light');

// Without Provider
function App() {
  return <ThemedDiv />; // Uses 'light' default
}

// With Provider
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedDiv /> {/* Uses 'dark' from Provider */}
    </ThemeContext.Provider>
  );
}

function ThemedDiv() {
  const theme = useContext(ThemeContext);
  return <div className={theme}>Content</div>;
}

Performance Considerations

  • Context updates trigger re-renders of all consuming components
  • Use multiple contexts to separate concerns and reduce re-renders
  • Memoize context values when they’re computed or include functions
  • Consider using state management libraries for complex state
  • useContext hook - Consume context in function components
  • Component - Class components with contextType
  • Fragment - Group elements without wrapper

Build docs developers (and LLMs) love