Skip to main content
Context provides a way to pass data through the component tree without having to pass props down manually at every level. It’s designed to share data that can be considered “global” for a tree of Preact components.

When to Use Context

Context is ideal for sharing data that is needed by many components at different nesting levels:
  • Current authenticated user
  • Theme preferences (dark/light mode)
  • Language/locale settings
  • UI state (modals, notifications)
Don’t use Context just to avoid passing props a few levels down. Context makes component reuse more difficult.

Creating Context

Use createContext to create a new context object. It accepts a default value that’s used when a component doesn’t have a matching Provider above it.
import { createContext } from 'preact';

const ThemeContext = createContext('light');

Implementation Details

The createContext function is implemented in src/create-context.js. It returns a special component that acts as both Provider and Consumer:
// Simplified from src/create-context.js
export function createContext(defaultValue) {
  function Context(props) {
    // Provider implementation
    return props.children;
  }
  
  Context._id = '__cC' + i++;
  Context._defaultValue = defaultValue;
  Context.Consumer = (props, contextValue) => props.children(contextValue);
  Context.Provider = Context;
  
  return Context;
}
Reference: src/create-context.js:6-60

Provider Component

Every Context object comes with a Provider component that allows consuming components to subscribe to context changes.
1
Basic Provider Usage
2
import { createContext } from 'preact';
import { useState } from 'preact/hooks';

const ThemeContext = createContext('light');

function App() {
  const [theme, setTheme] = useState('dark');

  return (
    <ThemeContext.Provider value={theme}>
      <Toolbar />
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
    </ThemeContext.Provider>
  );
}
3
Multiple Providers
4
You can nest multiple providers for different contexts:
5
const ThemeContext = createContext('light');
const UserContext = createContext(null);

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <UserContext.Provider value={{ name: 'Alice', role: 'admin' }}>
        <Dashboard />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}
6
Dynamic Values
7
Providers can accept any value, including objects and functions:
8
import { createContext } from 'preact';
import { useState } from 'preact/hooks';

const AuthContext = createContext(null);

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  const login = (username, password) => {
    // Login logic
    setUser({ username });
  };

  const logout = () => {
    setUser(null);
  };

  const value = {
    user,
    login,
    logout
  };

  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );
}

Consuming Context

There are two ways to consume context in Preact:

Practical Examples

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

const ThemeContext = createContext('light');

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}

// Usage
function App() {
  return (
    <ThemeProvider>
      <Header />
      <Main />
    </ThemeProvider>
  );
}

function Header() {
  const { theme, toggleTheme } = useTheme();
  
  return (
    <header className={theme}>
      <button onClick={toggleTheme}>
        Switch to {theme === 'light' ? 'dark' : 'light'} mode
      </button>
    </header>
  );
}

Best Practices

1
Split contexts by concern
2
Don’t put everything in one context. Split by logical boundaries:
3
// Good: Separate contexts
const ThemeContext = createContext();
const AuthContext = createContext();
const NotificationContext = createContext();

// Less ideal: One giant context
const AppContext = createContext({
  theme: 'light',
  user: null,
  notifications: []
});
4
Create custom hooks for context
5
Wrap useContext in custom hooks for better error handling and cleaner usage:
6
import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

const UserContext = createContext(null);

export function useUser() {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error('useUser must be used within UserProvider');
  }
  return context;
}
7
Optimize re-renders
8
When passing objects to Provider, memoize them to prevent unnecessary re-renders:
9
import { useMemo, useState } from 'preact/hooks';

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  // Good: Memoize the value object
  const value = useMemo(
    () => ({
      user,
      login: () => { /* ... */ },
      logout: () => { /* ... */ }
    }),
    [user]
  );

  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );
}
10
Use default values wisely
11
Default values are only used when there’s no Provider above. Use them for:
12
  • Development/testing without providers
  • Sensible fallbacks
  • 13
    // Good: Meaningful default
    const ThemeContext = createContext({
      theme: 'light',
      setTheme: () => console.warn('ThemeProvider not found')
    });
    
    // Less useful: null default requiring checks everywhere
    const ThemeContext = createContext(null);
    

    Performance Considerations

    Context updates cause all consuming components to re-render. Here are strategies to optimize:
    Split frequently changing values into separate contexts:
    // Instead of one context with both
    const AppContext = createContext({ theme, user });
    
    // Use separate contexts
    const ThemeContext = createContext('light');
    const UserContext = createContext(null);
    
    Now components that only need theme won’t re-render when user changes.

    Next Steps

    Hooks

    Learn more about hooks including useContext

    Refs

    Work with DOM references and forward refs

    Build docs developers (and LLMs) love