CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-preact

Fast 3kb React-compatible Virtual DOM library.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

components.mddocs/

Component System

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.

Capabilities

Component Base Class

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

Functional Components

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")
);

Component Lifecycle

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

Component Props and State Types

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

docs

compat.md

components.md

context.md

core.md

devtools.md

hooks.md

index.md

jsx-runtime.md

testing.md

tile.json