Fast 3kb React-compatible Virtual DOM library.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Class-based and functional component patterns with complete lifecycle methods and state management capabilities. Preact supports both React-style class components and modern functional components.
Base class for creating stateful components with lifecycle methods and local state management.
/**
* Base class for stateful components
*/
abstract class Component<P = {}, S = {}> {
constructor(props?: P, context?: any);
// Static properties
static displayName?: string;
static defaultProps?: any;
static contextType?: Context<any>;
// Static lifecycle methods
static getDerivedStateFromProps?(props: Readonly<object>, state: Readonly<object>): object | null;
static getDerivedStateFromError?(error: any): object | null;
// Instance properties
state: Readonly<S>;
props: RenderableProps<P>;
context: any;
base?: Element | Text;
// State management
setState<K extends keyof S>(
state: ((prevState: Readonly<S>, props: Readonly<P>) => Pick<S, K> | Partial<S> | null) |
(Pick<S, K> | Partial<S> | null),
callback?: () => void
): void;
forceUpdate(callback?: () => void): void;
// Lifecycle methods (optional)
componentWillMount?(): void;
componentDidMount?(): void;
componentWillUnmount?(): void;
componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;
shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean;
componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): void;
getSnapshotBeforeUpdate?(oldProps: Readonly<P>, oldState: Readonly<S>): any;
componentDidUpdate?(previousProps: Readonly<P>, previousState: Readonly<S>, snapshot: any): void;
componentDidCatch?(error: any, errorInfo: ErrorInfo): void;
getChildContext?(): object;
// Abstract render method
abstract render(props?: RenderableProps<P>, state?: Readonly<S>, context?: any): ComponentChildren;
}
interface ErrorInfo {
componentStack?: string;
}Usage Examples:
import { Component, createElement } from "preact";
// Basic class component
class Counter extends Component<{}, { count: number }> {
state = { count: 0 };
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return createElement("div", null,
createElement("p", null, `Count: ${this.state.count}`),
createElement("button", { onClick: this.increment }, "Increment")
);
}
}
// Component with props and lifecycle
interface TimerProps {
interval: number;
onTick: (count: number) => void;
}
interface TimerState {
count: number;
}
class Timer extends Component<TimerProps, TimerState> {
private intervalId?: number;
constructor(props: TimerProps) {
super(props);
this.state = { count: 0 };
}
componentDidMount() {
this.intervalId = window.setInterval(() => {
this.setState(prevState => ({ count: prevState.count + 1 }), () => {
this.props.onTick(this.state.count);
});
}, this.props.interval);
}
componentWillUnmount() {
if (this.intervalId) {
clearInterval(this.intervalId);
}
}
shouldComponentUpdate(nextProps: TimerProps, nextState: TimerState) {
return nextState.count !== this.state.count || nextProps.interval !== this.props.interval;
}
render() {
return createElement("div", null, `Timer: ${this.state.count}`);
}
}Function-based components for stateless presentation and modern hook-based logic.
/**
* Functional component interface
*/
interface FunctionComponent<P = {}> {
(props: RenderableProps<P>, context?: any): ComponentChildren;
displayName?: string;
defaultProps?: Partial<P> | undefined;
}
// Type alias
interface FunctionalComponent<P = {}> extends FunctionComponent<P> {}
// Generic component type
type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
// Component constructor interface
interface ComponentClass<P = {}, S = {}> {
new (props: P, context?: any): Component<P, S>;
displayName?: string;
defaultProps?: Partial<P>;
contextType?: Context<any>;
getDerivedStateFromProps?(props: Readonly<P>, state: Readonly<S>): Partial<S> | null;
getDerivedStateFromError?(error: any): Partial<S> | null;
}Usage Examples:
import { createElement, FunctionComponent } from "preact";
// Basic functional component
function Greeting({ name }: { name: string }) {
return createElement("h1", null, `Hello ${name}!`);
}
// Functional component with default props
const Button: FunctionComponent<{ text?: string; onClick?: () => void }> = ({
text = "Click me",
onClick
}) => {
return createElement("button", { onClick }, text);
};
Button.defaultProps = { text: "Default Text" };
// Component with children
interface CardProps {
title: string;
children?: ComponentChildren;
}
const Card: FunctionComponent<CardProps> = ({ title, children }) => {
return createElement("div", { className: "card" },
createElement("h2", { className: "card-title" }, title),
createElement("div", { className: "card-content" }, children)
);
};
// Using the card component
const app = createElement(Card, { title: "My Card" },
createElement("p", null, "This is the content"),
createElement("button", null, "Action")
);Detailed lifecycle methods available in class components for managing component behavior throughout its existence.
/**
* Component lifecycle methods
*/
interface Component<P = {}, S = {}> {
// Mounting phase
componentWillMount?(): void;
componentDidMount?(): void;
// Updating phase
componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;
shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean;
componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): void;
getSnapshotBeforeUpdate?(oldProps: Readonly<P>, oldState: Readonly<S>): any;
componentDidUpdate?(previousProps: Readonly<P>, previousState: Readonly<S>, snapshot: any): void;
// Unmounting phase
componentWillUnmount?(): void;
// Error handling
componentDidCatch?(error: any, errorInfo: ErrorInfo): void;
// Context
getChildContext?(): object;
}Usage Examples:
import { Component, createElement } from "preact";
class LifecycleDemo extends Component<{ userId: number }, { user: any; loading: boolean }> {
state = { user: null, loading: true };
async componentDidMount() {
console.log("Component mounted");
await this.fetchUser(this.props.userId);
}
componentWillReceiveProps(nextProps: { userId: number }) {
if (nextProps.userId !== this.props.userId) {
this.setState({ loading: true });
this.fetchUser(nextProps.userId);
}
}
shouldComponentUpdate(nextProps: { userId: number }, nextState: any) {
return nextProps.userId !== this.props.userId ||
nextState.user !== this.state.user ||
nextState.loading !== this.state.loading;
}
getSnapshotBeforeUpdate() {
return { scrollTop: document.documentElement.scrollTop };
}
componentDidUpdate(prevProps: { userId: number }, prevState: any, snapshot: any) {
console.log("Component updated", { prevProps, snapshot });
}
componentWillUnmount() {
console.log("Component will unmount");
// Cleanup subscriptions, timers, etc.
}
componentDidCatch(error: any, errorInfo: any) {
console.error("Component caught error:", error, errorInfo);
this.setState({ user: null, loading: false });
}
private async fetchUser(userId: number) {
try {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
this.setState({ user, loading: false });
} catch (error) {
this.setState({ user: null, loading: false });
}
}
render() {
if (this.state.loading) {
return createElement("div", null, "Loading...");
}
if (!this.state.user) {
return createElement("div", null, "User not found");
}
return createElement("div", null,
createElement("h1", null, this.state.user.name),
createElement("p", null, this.state.user.email)
);
}
}Type definitions for component properties, state, and related interfaces.
/**
* Props with children and ref support
*/
type RenderableProps<P, RefType = any> = P &
Readonly<Attributes & { children?: ComponentChildren; ref?: Ref<RefType> }>;
/**
* Props for specific component types
*/
type ComponentProps<C extends ComponentType<any> | keyof JSXInternal.IntrinsicElements> =
C extends ComponentType<infer P>
? P
: C extends keyof JSXInternal.IntrinsicElements
? JSXInternal.IntrinsicElements[C]
: {};
/**
* Base attributes interface
*/
interface Attributes {
key?: Key | undefined;
jsx?: boolean | undefined;
}
/**
* Attributes with ref support
*/
interface ClassAttributes<T> extends Attributes {
ref?: Ref<T>;
}
/**
* General component type union
*/
type AnyComponent<P = {}, S = {}> = FunctionComponent<P> | ComponentConstructor<P, S>;
interface ComponentConstructor<P = {}, S = {}> extends ComponentClass<P, S> {}Usage Examples:
import { ComponentProps, RenderableProps, createElement } from "preact";
// Using ComponentProps to extract props from existing components
type ButtonProps = ComponentProps<"button">;
type CustomButtonProps = ComponentProps<typeof CustomButton>;
// Component with properly typed props
interface UserCardProps {
user: {
id: number;
name: string;
email: string;
};
onEdit?: (id: number) => void;
}
function UserCard({ user, onEdit, children }: RenderableProps<UserCardProps>) {
return createElement("div", { className: "user-card" },
createElement("h3", null, user.name),
createElement("p", null, user.email),
onEdit && createElement("button",
{ onClick: () => onEdit(user.id) },
"Edit"
),
children
);
}
// Generic component with constraints
function List<T>({
items,
renderItem,
keyExtractor
}: RenderableProps<{
items: T[];
renderItem: (item: T, index: number) => ComponentChildren;
keyExtractor: (item: T, index: number) => Key;
}>) {
return createElement("ul", null,
items.map((item, index) =>
createElement("li",
{ key: keyExtractor(item, index) },
renderItem(item, index)
)
)
);
}Install with Tessl CLI
npx tessl i tessl/npm-preact