Fast 3kb React-compatible Virtual DOM library.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Development utilities, debugging helpers, and React DevTools integration for enhanced developer experience. These tools help with debugging, profiling, and understanding component behavior during development.
Development utilities for debugging and inspecting component behavior.
/**
* Resets the history of prop warnings
* Useful for clearing accumulated warnings in development
*/
function resetPropWarnings(): void;
/**
* Gets the currently rendering VNode
* @returns The VNode currently being processed or null
*/
function getCurrentVNode(): VNode | null;
/**
* Gets the display name of a component from a VNode
* @param vnode - VNode to extract display name from
* @returns Component display name or element type
*/
function getDisplayName(vnode: VNode): string;
/**
* Gets the component stack trace for debugging
* @param vnode - VNode to get stack trace for
* @returns String representation of component hierarchy
*/
function getOwnerStack(vnode: VNode): string;Usage Examples:
import {
resetPropWarnings,
getCurrentVNode,
getDisplayName,
getOwnerStack,
createElement
} from "preact/debug";
// Development debugging helper
function DebugInfo() {
const handleDebugClick = () => {
const currentVNode = getCurrentVNode();
if (currentVNode) {
console.log("Current VNode:", currentVNode);
console.log("Display Name:", getDisplayName(currentVNode));
console.log("Owner Stack:", getOwnerStack(currentVNode));
} else {
console.log("No VNode currently rendering");
}
};
const clearWarnings = () => {
resetPropWarnings();
console.log("Prop warnings cleared");
};
return createElement("div", { className: "debug-panel" },
createElement("h3", null, "Debug Tools"),
createElement("button", { onClick: handleDebugClick }, "Log Current VNode"),
createElement("button", { onClick: clearWarnings }, "Clear Prop Warnings")
);
}
// Custom hook for component debugging
function useComponentDebug(componentName: string) {
useEffect(() => {
console.log(`${componentName} mounted`);
return () => {
console.log(`${componentName} unmounted`);
};
}, [componentName]);
useEffect(() => {
const currentVNode = getCurrentVNode();
if (currentVNode) {
console.log(`${componentName} rendered:`, {
displayName: getDisplayName(currentVNode),
ownerStack: getOwnerStack(currentVNode)
});
}
});
}
// Component with debug information
function DebuggableComponent({ data }: { data: any }) {
useComponentDebug('DebuggableComponent');
return createElement("div", null,
createElement("h4", null, "Debuggable Component"),
createElement("pre", null, JSON.stringify(data, null, 2))
);
}React DevTools integration for component inspection and profiling.
/**
* Adds a custom name to a hook value for React DevTools display
* @param value - The hook value to name
* @param name - Custom name to display in DevTools
* @returns The original value unchanged
*/
function addHookName<T>(value: T, name: string): T;Usage Examples:
import { addHookName } from "preact/devtools";
import { useState, useEffect, useMemo } from "preact/hooks";
// Custom hook with DevTools naming
function useCounter(initialValue = 0, step = 1) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(c => c + step);
const decrement = () => setCount(c => c - step);
const reset = () => setCount(initialValue);
// These will show up with custom names in React DevTools
return {
count: addHookName(count, 'Count'),
increment: addHookName(increment, 'Increment'),
decrement: addHookName(decrement, 'Decrement'),
reset: addHookName(reset, 'Reset')
};
}
// Complex custom hook with multiple named values
function useApiData<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
let cancelled = false;
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
if (!cancelled) {
setData(result);
setLoading(false);
}
} catch (err) {
if (!cancelled) {
setError(err instanceof Error ? err : new Error(String(err)));
setLoading(false);
}
}
};
fetchData();
return () => {
cancelled = true;
};
}, [url]);
const refetch = () => {
setData(null);
setLoading(true);
setError(null);
};
// Named values for better DevTools experience
return {
data: addHookName(data, 'API Data'),
loading: addHookName(loading, 'Loading State'),
error: addHookName(error, 'Error State'),
refetch: addHookName(refetch, 'Refetch Function')
};
}
// Component using named hooks
function UserProfile({ userId }: { userId: number }) {
const { data: user, loading, error, refetch } = useApiData<User>(`/api/users/${userId}`);
const counter = useCounter(0, 1);
if (loading) return createElement("div", null, "Loading...");
if (error) return createElement("div", null, `Error: ${error.message}`);
if (!user) return createElement("div", null, "User not found");
return createElement("div", null,
createElement("h2", null, user.name),
createElement("p", null, user.email),
createElement("div", null,
createElement("p", null, `Counter: ${counter.count}`),
createElement("button", { onClick: counter.increment }, "+"),
createElement("button", { onClick: counter.decrement }, "-"),
createElement("button", { onClick: counter.reset }, "Reset")
),
createElement("button", { onClick: refetch }, "Refetch User")
);
}Global configuration options for development and debugging.
/**
* Global options object for customizing Preact behavior
*/
interface Options {
/** Hook invoked when a VNode is created */
vnode?(vnode: VNode): void;
/** Hook invoked before a VNode is unmounted */
unmount?(vnode: VNode): void;
/** Hook invoked after a VNode has been rendered/updated */
diffed?(vnode: VNode): void;
/** Event processing hook */
event?(e: Event): any;
/** Custom requestAnimationFrame implementation */
requestAnimationFrame?(callback: () => void): void;
/** Custom render batching function */
debounceRendering?(cb: () => void): void;
/** Custom debug value formatter */
useDebugValue?(value: string | number): void;
/** Internal hook name tracking */
_addHookName?(name: string | number): void;
/** Suspense resolution hook */
__suspenseDidResolve?(vnode: VNode, cb: () => void): void;
/** Custom attribute serialization for precompiled JSX */
attr?(name: string, value: any): string | void;
}
/**
* Global options object - can be modified to customize Preact behavior
*/
const options: Options;Usage Examples:
import { options } from "preact";
import { createElement } from "preact";
// Development logging setup
if (process.env.NODE_ENV === 'development') {
// Log all VNode creations
const originalVnode = options.vnode;
options.vnode = (vnode) => {
console.log('VNode created:', vnode.type, vnode.props);
originalVnode?.(vnode);
};
// Log component unmounts
const originalUnmount = options.unmount;
options.unmount = (vnode) => {
console.log('VNode unmounting:', vnode.type);
originalUnmount?.(vnode);
};
// Log render completions
const originalDiffed = options.diffed;
options.diffed = (vnode) => {
console.log('VNode diffed:', vnode.type);
originalDiffed?.(vnode);
};
// Custom debug value formatting
options.useDebugValue = (value) => {
if (typeof value === 'object') {
return JSON.stringify(value, null, 2);
}
return String(value);
};
}
// Performance monitoring
function setupPerformanceMonitoring() {
const renderTimes = new Map<string, number>();
const originalVnode = options.vnode;
options.vnode = (vnode) => {
if (typeof vnode.type === 'function') {
const componentName = vnode.type.displayName || vnode.type.name || 'Anonymous';
renderTimes.set(componentName, performance.now());
}
originalVnode?.(vnode);
};
const originalDiffed = options.diffed;
options.diffed = (vnode) => {
if (typeof vnode.type === 'function') {
const componentName = vnode.type.displayName || vnode.type.name || 'Anonymous';
const startTime = renderTimes.get(componentName);
if (startTime) {
const renderTime = performance.now() - startTime;
console.log(`${componentName} rendered in ${renderTime.toFixed(2)}ms`);
renderTimes.delete(componentName);
}
}
originalDiffed?.(vnode);
};
}
// Custom event handling
function setupEventLogging() {
const originalEvent = options.event;
options.event = (e) => {
// Log all events in development
if (process.env.NODE_ENV === 'development') {
console.log('Event:', e.type, e.target);
}
// Call original event handler if it exists
return originalEvent?.(e);
};
}
// Custom render batching for testing
function setupTestBatching() {
options.debounceRendering = (callback) => {
// Immediate rendering for tests
callback();
};
}
// Component that uses global options
function DebuggedApp() {
useEffect(() => {
if (process.env.NODE_ENV === 'development') {
setupPerformanceMonitoring();
setupEventLogging();
}
}, []);
return createElement("div", null,
createElement("h1", null, "Debugged Application"),
createElement("button", {
onClick: () => console.log("Button clicked")
}, "Test Button")
);
}Utilities and patterns for setting up effective development environments.
Usage Examples:
// Development environment configuration
function setupDevelopmentEnvironment() {
// Auto-import debug module in development
if (process.env.NODE_ENV === 'development') {
import('preact/debug').then(() => {
console.log('Preact debug mode enabled');
});
// Import devtools integration
import('preact/devtools').then(() => {
console.log('React DevTools integration enabled');
});
}
}
// Error boundary with debug information
class DebugErrorBoundary extends Component<
{ children: ComponentChildren; fallback?: ComponentChildren },
{ hasError: boolean; error?: Error }
> {
state = { hasError: false };
static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: any) {
console.error('Error caught by boundary:', error);
console.error('Error info:', errorInfo);
if (process.env.NODE_ENV === 'development') {
const currentVNode = getCurrentVNode();
if (currentVNode) {
console.error('Error in component:', getDisplayName(currentVNode));
console.error('Component stack:', getOwnerStack(currentVNode));
}
}
}
render() {
if (this.state.hasError) {
return this.props.fallback || createElement("div", null,
createElement("h2", null, "Something went wrong"),
process.env.NODE_ENV === 'development' &&
createElement("pre", null, this.state.error?.stack)
);
}
return this.props.children;
}
}
// Development-only component inspector
function ComponentInspector({ children }: { children: ComponentChildren }) {
if (process.env.NODE_ENV !== 'development') {
return children;
}
return createElement("dev-tools", {
style: { position: 'relative' }
},
children,
createElement(DebugInfo)
);
}Install with Tessl CLI
npx tessl i tessl/npm-preact