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
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>;
}
- 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