Skip to main content
Base class for creating stateful, class-based components in Preact.

Signature

abstract class Component<P = {}, S = {}> {
  constructor(props?: P, context?: any);
  
  // Instance properties
  props: RenderableProps<P>;
  state: Readonly<S>;
  context: any;
  
  // Core methods
  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;
  
  abstract render(
    props?: RenderableProps<P>,
    state?: Readonly<S>,
    context?: any
  ): ComponentChildren;
  
  // Lifecycle methods
  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;
  
  // Static methods
  static displayName?: string;
  static contextType?: Context<any>;
  static getDerivedStateFromProps?(props: Readonly<object>, state: Readonly<object>): object | null;
  static getDerivedStateFromError?(error: any): object | null;
}

Type Parameters

P
object
default:"{}"
The props type for the component.
S
object
default:"{}"
The state type for the component.

Constructor

constructor(props?: P, context?: any)
Creates a new component instance with the given props and context.

Instance Properties

props

props: RenderableProps<P>
The current props passed to the component. Includes children and optional ref.

state

state: Readonly<S>
The current state of the component. Should only be modified via setState().

context

context: any
Context value from the nearest Provider ancestor, or the default value.

Core Methods

setState

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
Updates component state and schedules a re-render. Parameters:
  • state: Object to merge into state, or updater function receiving previous state and props
  • callback: Optional function called after state update and re-render complete
Implementation: src/component.js:34

forceUpdate

forceUpdate(callback?: () => void): void
Forces a synchronous re-render, bypassing shouldComponentUpdate. Parameters:
  • callback: Optional function called after re-render completes
Implementation: src/component.js:69

render

abstract render(
  props?: RenderableProps<P>,
  state?: Readonly<S>,
  context?: any
): ComponentChildren
Returns the component’s virtual DOM tree. Must be implemented by subclasses.

Usage Examples

Basic Component

import { Component } from 'preact';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  
  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Increment
        </button>
      </div>
    );
  }
}

With TypeScript

import { Component } from 'preact';

interface Props {
  initialCount: number;
  onCountChange?: (count: number) => void;
}

interface State {
  count: number;
}

class Counter extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { count: props.initialCount };
  }
  
  increment = () => {
    this.setState(
      (prevState) => ({ count: prevState.count + 1 }),
      () => {
        this.props.onCountChange?.(this.state.count);
      }
    );
  }
  
  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

Lifecycle Methods

import { Component } from 'preact';

class DataFetcher extends Component {
  state = { data: null, loading: true, error: null };
  
  componentDidMount() {
    // Fetch data after mount
    fetch('/api/data')
      .then(res => res.json())
      .then(data => this.setState({ data, loading: false }))
      .catch(error => this.setState({ error, loading: false }));
  }
  
  componentWillUnmount() {
    // Cleanup subscriptions, timers, etc.
    this.cleanup();
  }
  
  componentDidUpdate(prevProps, prevState) {
    // Respond to prop or state changes
    if (prevProps.id !== this.props.id) {
      this.fetchData(this.props.id);
    }
  }
  
  render() {
    const { data, loading, error } = this.state;
    
    if (loading) return <div>Loading...</div>;
    if (error) return <div>Error: {error.message}</div>;
    return <div>{JSON.stringify(data)}</div>;
  }
}

shouldComponentUpdate Optimization

import { Component } from 'preact';

class OptimizedList extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    // Only re-render if items array reference changed
    return this.props.items !== nextProps.items;
  }
  
  render() {
    return (
      <ul>
        {this.props.items.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    );
  }
}

Error Boundaries

import { Component } from 'preact';

class ErrorBoundary extends Component {
  state = { hasError: false, error: null };
  
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }
  
  componentDidCatch(error, errorInfo) {
    // Log error to service
    console.error('Error caught:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h1>Something went wrong</h1>
          <details>
            <summary>Error details</summary>
            <pre>{this.state.error.toString()}</pre>
          </details>
        </div>
      );
    }
    
    return this.props.children;
  }
}

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

Lifecycle Method Order

Mounting

  1. constructor()
  2. static getDerivedStateFromProps()
  3. render()
  4. componentDidMount()

Updating

  1. static getDerivedStateFromProps()
  2. shouldComponentUpdate()
  3. render()
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate()

Unmounting

  1. componentWillUnmount()

Error Handling

  1. static getDerivedStateFromError()
  2. componentDidCatch()

Legacy Lifecycle Methods

These methods are still supported but deprecated:
  • componentWillMount() - Use componentDidMount() or constructor()
  • componentWillReceiveProps() - Use getDerivedStateFromProps() or componentDidUpdate()
  • componentWillUpdate() - Use getSnapshotBeforeUpdate() or componentDidUpdate()

Implementation Details

The Component class is implemented in src/component.js:19 as BaseComponent:
export function BaseComponent(props, context) {
  this.props = props;
  this.context = context;
  this._bits = 0;
}

BaseComponent.prototype.setState = function (update, callback) {
  // Implementation...
};

BaseComponent.prototype.forceUpdate = function (callback) {
  // Implementation...
};

BaseComponent.prototype.render = Fragment;

Performance Notes

  • setState uses shallow merging and batches multiple calls
  • Re-renders are scheduled asynchronously via microtasks
  • forceUpdate bypasses shouldComponentUpdate optimization
  • State updates within setState callback see the updated state
  • render - Mount components to the DOM
  • Fragment - Group multiple children
  • createContext - Create context for component tree
  • hooks - Modern alternative to class components

Build docs developers (and LLMs) love