Skip to main content
Components are the building blocks of Preact applications. They let you split your UI into independent, reusable pieces.

Component types

Preact supports two types of components:
Functional components are simple JavaScript functions that accept props and return JSX.
function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}
This is the recommended approach for most components. They are simpler, easier to test, and work seamlessly with hooks.

The Component class

Preact exports a BaseComponent class (aliased as Component) that provides core functionality for class-based components. Here’s how it’s defined in src/component.js:19:
export function BaseComponent(props, context) {
  this.props = props;
  this.context = context;
  this._bits = 0;
}
The Component class provides two key methods:

setState()

Updates component state and schedules a re-render:
BaseComponent.prototype.setState = function (update, callback) {
  let s;
  if (this._nextState != NULL && this._nextState != this.state) {
    s = this._nextState;
  } else {
    s = this._nextState = assign({}, this.state);
  }

  if (typeof update == 'function') {
    update = update(assign({}, s), this.props);
  }

  if (update) {
    assign(s, update);
  } else {
    return;
  }

  if (this._vnode) {
    if (callback) {
      this._stateCallbacks.push(callback);
    }
    enqueueRender(this);
  }
};

forceUpdate()

Immediately triggers a re-render, bypassing shouldComponentUpdate():
BaseComponent.prototype.forceUpdate = function (callback) {
  if (this._vnode) {
    this._bits |= COMPONENT_FORCE;
    if (callback) this._renderCallbacks.push(callback);
    enqueueRender(this);
  }
};

Props

Props (short for “properties”) are arguments passed to components. They are read-only and flow down from parent to child.
function Welcome({ name, age }) {
  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
    </div>
  );
}

// Usage
<Welcome name="Alice" age={30} />

Props in class components

In class components, props are available via this.props:
import { Component } from 'preact';

class Welcome extends Component {
  render() {
    return (
      <div>
        <p>Name: {this.props.name}</p>
        <p>Age: {this.props.age}</p>
      </div>
    );
  }
}

State

State is private data managed within a component. When state changes, the component re-renders.

State in class components

Here’s a real example from Preact’s demo code (demo/todo.jsx:5):
import { Component } from 'preact';

class TodoList extends Component {
  state = { todos: [], text: '' };

  setText = e => {
    this.setState({ text: e.target.value });
  };

  addTodo = () => {
    let { todos, text } = this.state;
    todos = todos.concat({ text, id: ++counter });
    this.setState({ todos, text: '' });
  };

  render({}, { todos, text }) {
    return (
      <form onSubmit={this.addTodo} action="javascript:">
        <input value={text} onInput={this.setText} />
        <button type="submit">Add</button>
        <ul>
          {todos.map(todo => (
            <li key={todo.id}>{todo.text}</li>
          ))}
        </ul>
      </form>
    );
  }
}
The render() method can destructure both props and state from its arguments for cleaner code.

Updating state

You can pass an object or a function to setState():
this.setState({ count: 5 });
Never modify state directly. Always use setState() to ensure the component re-renders correctly.

Functional vs class components

Here’s the same component implemented both ways:
import { useState } from 'preact/hooks';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

Real-world example

Here’s a complete example from Preact’s demo showing both component types working together (demo/context.jsx:5):
import { Component, createContext } from 'preact';
const { Provider, Consumer } = createContext();

class ThemeProvider extends Component {
  state = {
    value: this.props.value
  };

  onClick = () => {
    this.setState(prev => ({
      value: prev.value === this.props.value 
        ? this.props.next 
        : this.props.value
    }));
  };

  render() {
    return (
      <div>
        <button onClick={this.onClick}>Toggle</button>
        <Provider value={this.state.value}>
          {this.props.children}
        </Provider>
      </div>
    );
  }
}

class Child extends Component {
  shouldComponentUpdate() {
    return false;
  }

  render() {
    return (
      <>
        <p>(blocked update)</p>
        {this.props.children}
      </>
    );
  }
}
This example demonstrates prop passing, state management, lifecycle methods, and component composition.

Component render queue

When you call setState() or forceUpdate(), Preact doesn’t immediately re-render the component. Instead, it adds the component to a render queue (src/component.js:185):
let rerenderQueue = [];

export function enqueueRender(c) {
  if (
    (!(c._bits & COMPONENT_DIRTY) &&
      (c._bits |= COMPONENT_DIRTY) &&
      rerenderQueue.push(c) &&
      !rerenderCount++) ||
    prevDebounce != options.debounceRendering
  ) {
    prevDebounce = options.debounceRendering;
    (prevDebounce || queueMicrotask)(process);
  }
}
This batching mechanism ensures efficient rendering by processing multiple updates together.

Build docs developers (and LLMs) love