Fast 3kb React-compatible Virtual DOM library.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Provider/consumer pattern for sharing data across component hierarchies without prop drilling. Preact's Context API provides efficient state management for deeply nested components.
Creates context objects that enable data sharing between components at different levels of the component tree.
/**
* Creates a new context with a default value
* @param defaultValue - Default value used when no Provider is found
* @returns Context object with Provider and Consumer components
*/
function createContext<T>(defaultValue: T): Context<T>;
/**
* Context object interface
*/
interface Context<T> {
Provider: ComponentType<ProviderProps<T>>;
Consumer: ComponentType<ConsumerProps<T>>;
displayName?: string;
}
interface ProviderProps<T> {
value: T;
children?: ComponentChildren;
}
interface ConsumerProps<T> {
children: (value: T) => ComponentChildren;
}
// Type aliases for compatibility
interface PreactContext<T> extends Context<T> {}
interface PreactProvider<T> extends Provider<T> {}
interface PreactConsumer<T> extends Consumer<T> {}
/**
* Extract context value type from Context
*/
type ContextType<C extends Context<any>> = C extends Context<infer T> ? T : never;Usage Examples:
import { createContext, createElement, useState } from "preact";
import { useContext } from "preact/hooks";
// Theme context
interface ThemeContextValue {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextValue>({
theme: 'light',
toggleTheme: () => {}
});
// Theme provider component
function ThemeProvider({ children }: { children: ComponentChildren }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
const value = { theme, toggleTheme };
return createElement(ThemeContext.Provider, { value }, children);
}
// Component using context with hook
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return createElement("button", {
onClick: toggleTheme,
style: {
backgroundColor: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#333' : '#fff',
border: `1px solid ${theme === 'light' ? '#333' : '#fff'}`
}
}, `Switch to ${theme === 'light' ? 'dark' : 'light'} theme`);
}
// Component using context with Consumer
function ThemedText() {
return createElement(ThemeContext.Consumer, null, ({ theme }) =>
createElement("p", {
style: {
color: theme === 'light' ? '#333' : '#fff'
}
}, `Current theme: ${theme}`)
);
}
// App with theme provider
function App() {
return createElement(ThemeProvider, null,
createElement("div", null,
createElement("h1", null, "Themed App"),
createElement(ThemedButton),
createElement(ThemedText)
)
);
}Component that provides context values to descendant components.
/**
* Provider component interface
*/
interface Provider<T> extends FunctionComponent<{
value: T;
children?: ComponentChildren;
}> {}
/**
* Provider component that makes context value available to descendants
* Automatically included in Context object from createContext
*/Usage Examples:
import { createContext, createElement, useState } from "preact";
// User authentication context
interface User {
id: number;
name: string;
email: string;
}
interface AuthContextValue {
user: User | null;
login: (user: User) => void;
logout: () => void;
isAuthenticated: boolean;
}
const AuthContext = createContext<AuthContextValue>({
user: null,
login: () => {},
logout: () => {},
isAuthenticated: false
});
// Authentication provider
function AuthProvider({ children }: { children: ComponentChildren }) {
const [user, setUser] = useState<User | null>(null);
const login = (userData: User) => {
setUser(userData);
localStorage.setItem('user', JSON.stringify(userData));
};
const logout = () => {
setUser(null);
localStorage.removeItem('user');
};
const contextValue: AuthContextValue = {
user,
login,
logout,
isAuthenticated: user !== null
};
return createElement(AuthContext.Provider, { value: contextValue }, children);
}
// Multiple nested providers
function AppProviders({ children }: { children: ComponentChildren }) {
return createElement(AuthProvider, null,
createElement(ThemeProvider, null,
createElement(LocaleProvider, null,
children
)
)
);
}
// Provider with complex state management
interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
}
interface CartContextValue {
items: CartItem[];
addItem: (item: Omit<CartItem, 'quantity'>) => void;
removeItem: (id: number) => void;
updateQuantity: (id: number, quantity: number) => void;
total: number;
itemCount: number;
}
const CartContext = createContext<CartContextValue>({
items: [],
addItem: () => {},
removeItem: () => {},
updateQuantity: () => {},
total: 0,
itemCount: 0
});
function CartProvider({ children }: { children: ComponentChildren }) {
const [items, setItems] = useState<CartItem[]>([]);
const addItem = (newItem: Omit<CartItem, 'quantity'>) => {
setItems(prevItems => {
const existingItem = prevItems.find(item => item.id === newItem.id);
if (existingItem) {
return prevItems.map(item =>
item.id === newItem.id
? { ...item, quantity: item.quantity + 1 }
: item
);
}
return [...prevItems, { ...newItem, quantity: 1 }];
});
};
const removeItem = (id: number) => {
setItems(prevItems => prevItems.filter(item => item.id !== id));
};
const updateQuantity = (id: number, quantity: number) => {
if (quantity <= 0) {
removeItem(id);
return;
}
setItems(prevItems =>
prevItems.map(item =>
item.id === id ? { ...item, quantity } : item
)
);
};
const total = items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
const itemCount = items.reduce((count, item) => count + item.quantity, 0);
const contextValue: CartContextValue = {
items,
addItem,
removeItem,
updateQuantity,
total,
itemCount
};
return createElement(CartContext.Provider, { value: contextValue }, children);
}Component that consumes context values using render props pattern.
/**
* Consumer component interface
*/
interface Consumer<T> extends FunctionComponent<{
children: (value: T) => ComponentChildren;
}> {}
/**
* Consumer component that provides context value to render function
* Automatically included in Context object from createContext
*/Usage Examples:
import { createContext, createElement } from "preact";
// Using Consumer with render props
function UserProfile() {
return createElement(AuthContext.Consumer, null, ({ user, isAuthenticated }) => {
if (!isAuthenticated) {
return createElement("div", null, "Please log in");
}
return createElement("div", { className: "user-profile" },
createElement("h2", null, user.name),
createElement("p", null, user.email),
createElement("img", {
src: `https://api.dicebear.com/7.x/initials/svg?seed=${user.name}`,
alt: `${user.name}'s avatar`
})
);
});
}
// Consumer with conditional rendering
function ShoppingCartIcon() {
return createElement(CartContext.Consumer, null, ({ itemCount }) =>
createElement("div", { className: "cart-icon" },
createElement("span", null, "🛒"),
itemCount > 0 && createElement("span", { className: "badge" }, itemCount)
)
);
}
// Nested consumers
function UserDashboard() {
return createElement(AuthContext.Consumer, null, ({ user, isAuthenticated }) =>
createElement(ThemeContext.Consumer, null, ({ theme }) =>
createElement(CartContext.Consumer, null, ({ total, itemCount }) => {
if (!isAuthenticated) {
return createElement("div", null, "Access denied");
}
return createElement("div", {
className: `dashboard theme-${theme}`
},
createElement("h1", null, `Welcome, ${user.name}!`),
createElement("p", null, `Cart: ${itemCount} items ($${total.toFixed(2)})`),
createElement("p", null, `Theme: ${theme}`)
);
})
)
);
}
// Consumer with error handling
function SafeConsumer() {
return createElement(AuthContext.Consumer, null, (contextValue) => {
try {
if (!contextValue) {
return createElement("div", null, "Context not available");
}
const { user, isAuthenticated } = contextValue;
return createElement("div", null,
isAuthenticated
? `Hello, ${user?.name}`
: "Not logged in"
);
} catch (error) {
return createElement("div", null, "Error loading user data");
}
});
}Patterns and utilities for effective context usage.
/**
* Custom hook pattern for context consumption
* Provides better error handling and type safety
*/
function useCustomContext<T>(context: Context<T>, errorMessage?: string): T {
const contextValue = useContext(context);
if (contextValue === undefined) {
throw new Error(errorMessage || 'useCustomContext must be used within a Provider');
}
return contextValue;
}Usage Examples:
import { createContext, createElement, useContext } from "preact";
// Custom hook for safer context consumption
function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
// Context with reducer pattern
interface AppState {
user: User | null;
theme: 'light' | 'dark';
locale: string;
}
type AppAction =
| { type: 'SET_USER'; user: User | null }
| { type: 'TOGGLE_THEME' }
| { type: 'SET_LOCALE'; locale: string };
function appReducer(state: AppState, action: AppAction): AppState {
switch (action.type) {
case 'SET_USER':
return { ...state, user: action.user };
case 'TOGGLE_THEME':
return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' };
case 'SET_LOCALE':
return { ...state, locale: action.locale };
default:
return state;
}
}
interface AppContextValue {
state: AppState;
dispatch: (action: AppAction) => void;
}
const AppContext = createContext<AppContextValue | null>(null);
function AppProvider({ children }: { children: ComponentChildren }) {
const [state, dispatch] = useReducer(appReducer, {
user: null,
theme: 'light',
locale: 'en'
});
return createElement(AppContext.Provider, {
value: { state, dispatch }
}, children);
}
function useApp() {
const context = useContext(AppContext);
if (!context) {
throw new Error('useApp must be used within an AppProvider');
}
return context;
}
// Component using the app context
function AppHeader() {
const { state, dispatch } = useApp();
return createElement("header", {
className: `header theme-${state.theme}`
},
createElement("h1", null, "My App"),
state.user && createElement("span", null, `Welcome, ${state.user.name}`),
createElement("button", {
onClick: () => dispatch({ type: 'TOGGLE_THEME' })
}, `Switch to ${state.theme === 'light' ? 'dark' : 'light'} theme`)
);
}
// Context composition pattern
function Providers({ children }: { children: ComponentChildren }) {
return createElement(AppProvider, null,
createElement(AuthProvider, null,
createElement(CartProvider, null,
children
)
)
);
}Install with Tessl CLI
npx tessl i tessl/npm-preact