> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/preactjs/preact/llms.txt
> Use this file to discover all available pages before exploring further.

# Context API

> Share data across your component tree without prop drilling

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)

<Warning>
  Don't use Context just to avoid passing props a few levels down. Context makes component reuse more difficult.
</Warning>

## 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.

```jsx theme={null}
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:

```javascript theme={null}
// 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.

<Steps>
  ### Basic Provider Usage

  ```jsx theme={null}
  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>
    );
  }
  ```

  ### Multiple Providers

  You can nest multiple providers for different contexts:

  ```jsx theme={null}
  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>
    );
  }
  ```

  ### Dynamic Values

  Providers can accept any value, including objects and functions:

  ```jsx theme={null}
  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>
    );
  }
  ```
</Steps>

## Consuming Context

There are two ways to consume context in Preact:

<Tabs>
  <Tab title="useContext Hook (Recommended)">
    The `useContext` hook is the recommended way to consume context in function components:

    ```jsx theme={null}
    import { createContext } from 'preact';
    import { useContext } from 'preact/hooks';

    const ThemeContext = createContext('light');

    function ThemedButton() {
      const theme = useContext(ThemeContext);
      
      return (
        <button className={`btn-${theme}`}>
          Button
        </button>
      );
    }
    ```

    ### How useContext Works

    The `useContext` hook is implemented in `hooks/src/index.js`. It reads the context value from the current component's context and subscribes to updates:

    ```javascript theme={null}
    // Simplified from hooks/src/index.js:367-385
    export function useContext(context) {
      const provider = currentComponent.context[context._id];
      const state = getHookState(currentIndex++, 9);
      
      if (!provider) return context._defaultValue;
      
      if (state._value == null) {
        state._value = true;
        provider.sub(currentComponent);  // Subscribe to updates
      }
      
      return provider.props.value;
    }
    ```

    Reference: `hooks/src/index.js:367-385`
  </Tab>

  <Tab title="Consumer Component">
    You can also use the Consumer component with a render prop pattern:

    ```jsx theme={null}
    import { createContext } from 'preact';

    const ThemeContext = createContext('light');

    function ThemedButton() {
      return (
        <ThemeContext.Consumer>
          {theme => (
            <button className={`btn-${theme}`}>
              Button
            </button>
          )}
        </ThemeContext.Consumer>
      );
    }
    ```

    <Note>
      The Consumer pattern is less common now that hooks are available. Use `useContext` for cleaner code.
    </Note>
  </Tab>
</Tabs>

## Practical Examples

<CodeGroup>
  ```jsx Theme Context theme={null}
  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>
    );
  }
  ```

  ```jsx Auth Context theme={null}
  import { createContext } from 'preact';
  import { useContext, useState, useEffect } from 'preact/hooks';

  const AuthContext = createContext(null);

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

    useEffect(() => {
      // Check for existing session
      fetch('/api/me')
        .then(res => res.json())
        .then(data => {
          setUser(data.user);
          setLoading(false);
        })
        .catch(() => setLoading(false));
    }, []);

    const login = async (email, password) => {
      const res = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password })
      });
      const data = await res.json();
      setUser(data.user);
    };

    const logout = async () => {
      await fetch('/api/logout', { method: 'POST' });
      setUser(null);
    };

    return (
      <AuthContext.Provider value={{ user, login, logout, loading }}>
        {children}
      </AuthContext.Provider>
    );
  }

  export function useAuth() {
    return useContext(AuthContext);
  }

  // Usage
  function ProtectedRoute({ children }) {
    const { user, loading } = useAuth();

    if (loading) return <div>Loading...</div>;
    if (!user) return <Login />;
    return children;
  }
  ```

  ```jsx Multi-Context theme={null}
  import { createContext } from 'preact';
  import { useContext } from 'preact/hooks';

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

  // Combine multiple contexts
  function AppProviders({ children }) {
    return (
      <ThemeProvider>
        <LanguageProvider>
          <UserProvider>
            {children}
          </UserProvider>
        </LanguageProvider>
      </ThemeProvider>
    );
  }

  // Use multiple contexts
  function Component() {
    const theme = useContext(ThemeContext);
    const language = useContext(LanguageContext);
    const user = useContext(UserContext);

    return (
      <div className={theme}>
        <p>Language: {language}</p>
        <p>User: {user?.name}</p>
      </div>
    );
  }
  ```
</CodeGroup>

## Best Practices

<Steps>
  ### Split contexts by concern

  Don't put everything in one context. Split by logical boundaries:

  ```jsx theme={null}
  // 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: []
  });
  ```

  ### Create custom hooks for context

  Wrap `useContext` in custom hooks for better error handling and cleaner usage:

  ```jsx theme={null}
  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;
  }
  ```

  ### Optimize re-renders

  When passing objects to Provider, memoize them to prevent unnecessary re-renders:

  ```jsx theme={null}
  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>
    );
  }
  ```

  ### Use default values wisely

  Default values are only used when there's no Provider above. Use them for:

  * Development/testing without providers
  * Sensible fallbacks

  ```jsx theme={null}
  // 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);
  ```
</Steps>

## Performance Considerations

Context updates cause all consuming components to re-render. Here are strategies to optimize:

<Tabs>
  <Tab title="Split Contexts">
    Split frequently changing values into separate contexts:

    ```jsx theme={null}
    // 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.
  </Tab>

  <Tab title="Memoize Context Values">
    Use `useMemo` to prevent creating new objects on every render:

    ```jsx theme={null}
    function Provider({ children }) {
      const [state, setState] = useState(initialState);

      const value = useMemo(() => ({
        state,
        setState
      }), [state]);

      return <Context.Provider value={value}>{children}</Context.Provider>;
    }
    ```
  </Tab>

  <Tab title="Component Splitting">
    Split components so only parts that need context re-render:

    ```jsx theme={null}
    // Before: Entire component re-renders on context change
    function Profile() {
      const user = useContext(UserContext);
      return (
        <div>
          <ExpensiveComponent />
          <p>{user.name}</p>
        </div>
      );
    }

    // After: Only UserName re-renders
    function Profile() {
      return (
        <div>
          <ExpensiveComponent />
          <UserName />
        </div>
      );
    }

    function UserName() {
      const user = useContext(UserContext);
      return <p>{user.name}</p>;
    }
    ```
  </Tab>
</Tabs>

## Next Steps

<CardGroup cols={2}>
  <Card title="Hooks" icon="hook" href="/guides/working-with-hooks">
    Learn more about hooks including useContext
  </Card>

  <Card title="Refs" icon="link" href="/guides/refs">
    Work with DOM references and forward refs
  </Card>
</CardGroup>
