Skip to main content
Preact is a fast 3kB alternative to React with the same modern API. It delivers the same component-based architecture and hooks as React, but with a significantly smaller footprint and better performance. When you want React’s developer experience without the bundle size overhead, Preact is the perfect choice.

Why Preact?

Preact brings all the power of modern React development with exceptional performance: Tiny Bundle Size - Only 3kB gzipped vs React’s ~45kB. Perfect for mobile-first applications. Blazing Fast - Smaller runtime means faster parsing, faster execution, and better performance on low-end devices. React Compatible - Use preact/compat to run most React libraries unchanged. Same hooks API, same patterns. Modern Features - Hooks, fragments, context, concurrent rendering, and more. Signals - Unique fine-grained reactivity system that’s even faster than hooks. No Build Required - Can run directly in browsers with ES modules (though bundling is recommended).

Preact vs React Comparison

Bundle Size Comparison

Real-world bundle sizes (production, gzipped):
This means:
  • Faster initial load (especially on slow connections)
  • Better performance on mobile devices
  • Lower bandwidth costs
  • Improved Core Web Vitals scores

Quick Start

Create a new Preact project with the CLI:
Visit http://localhost:3000 to see your app!

Project Structure

Configuration

Vite Configuration

Configure Vite for Preact with React compatibility:

frontend/vite.config.js

Why use aliases?
  • Use existing React libraries without modification
  • Gradual migration from React to Preact
  • Access to React ecosystem (react-router, etc.)

Backend Configuration

app/server/app.go

Preact vs React: Key Differences

While Preact is largely compatible with React, there are some differences:

1. Import Paths

2. JSX Pragma

Preact uses h function instead of React.createElement:
With @preact/preset-vite, you don’t need to import h manually.

3. Event Naming

Preact uses standard DOM event names:
Important: Preact uses onInput for real-time updates, while React uses onChange.

4. Class Names

Both support className, but Preact also supports class:

5. defaultValue vs value

Components

Function Components

Class Components

While hooks are preferred, class components work too:

Hooks

Preact supports all React hooks:

useState

useEffect

useReducer

useContext

useMemo and useCallback

useRef

Custom Hooks

Usage:

Preact Signals

Signals are Preact’s unique fine-grained reactivity system. They’re faster and simpler than hooks for state management.

What are Signals?

Signals are reactive primitives that automatically update components when their value changes:

Why Signals?

Performance:
  • No re-renders! Components only update the specific DOM nodes that changed
  • Skip Virtual DOM diffing
  • Faster than useState for frequently updating state
Simplicity:
  • No dependency arrays
  • No useMemo/useCallback needed
  • Share state without Context API
Size:
  • Signals add only ~1.6kB to your bundle

Basic Signals

Key points:
  • Access/update with .value in JavaScript
  • Use signal directly in JSX (no .value needed)
  • Changes trigger automatic, fine-grained updates

Computed Signals

Derived values that automatically update:
Computed signals:
  • Only recalculate when dependencies change
  • Cached automatically
  • Can depend on other computed signals

Effect Signal

Run side effects when signals change:

Signals for State Management

Create a global store with signals:
Use in components:

Signals vs Hooks

Rule of thumb:
  • Use Signals for: Global state, frequently updated values, shared state
  • Use Hooks for: Local component state, one-time effects, familiar patterns

Routing

Preact doesn’t include routing, but you have options: Lightweight routing made for Preact:
Access route params:
Programmatic navigation:

Option 2: React Router (with compat)

Use React Router with preact/compat:

Option 3: wouter (Minimalist)

Tiny routing library:

React Compatibility (preact/compat)

Run most React libraries unchanged with preact/compat.

Setup

Already configured in the Vite config above. Just install React libraries:
Import as normal:

What Works

Most React libraries work out of the box: βœ… Routing: react-router-dom, wouter βœ… Forms: react-hook-form, formik βœ… State: zustand, jotai βœ… Data Fetching: react-query, swr βœ… UI Libraries: Many work (test first) βœ… Styling: styled-components, emotion

What Doesn’t Work

❌ React-specific internals: Libraries using React internals ❌ React Native: Web only ❌ Some UI libraries: Material-UI, Chakra (use Preact alternatives)

Testing Compatibility

To test if a library works:
If it renders without errors, it works!

Styling

CSS Modules

Tailwind CSS

Install Tailwind:
Configure tailwind.config.js:
Use in components:

Styled Components

Development Workflow

Starting Development

Visit http://localhost:3000

Hot Module Replacement

Preact supports Fast Refresh:
  • Edit components β†’ instant updates
  • State preserved during updates
  • No full page reload

DevTools

Install Preact DevTools extension: Features:
  • Inspect component tree
  • View props and state
  • Track re-renders
  • Performance profiling

Building for Production

This:
  1. Builds Preact app (npm run build)
  2. Builds Go binary with embedded frontend

Build Optimizations

The Preact template includes optimizations: Code splitting:
Tree shaking:
Bundle analysis:

Production Checklist

  • Remove console.logs
  • Enable minification
  • Optimize images
  • Lazy load routes
  • Use Signals for global state
  • Check bundle size (aim for <15kB with routing)
  • Test on slow connections
  • Verify DevTools disabled in prod

TypeScript

Preact has excellent TypeScript support:

Typed Hooks

Typed Signals

Real-World Example: Todo App with Signals

Complete todo app using Signals:

Backend

Store with Signals

Components

Migration from React

Step 1: Install Preact

Step 2: Update Vite Config

Step 3: Update Imports (Gradual)

Step 4: Test

Run your app. Most things should work immediately!

Step 5: Optimize (Optional)

Replace compat imports with Preact native:
Replace React Router with preact-router:
Consider using Signals for state management.

Performance Tips

1. Use Signals for Shared State

2. Lazy Load Routes

3. Avoid Inline Functions

4. Use Keys in Lists

5. Measure Bundle Size

Aim for:
  • Total JS: <20kB (with routing)
  • Main bundle: <15kB

Troubleshooting

h is not defined

Error:
Cause: Not using @preact/preset-vite Solution: Install preset:
Update vite.config.js:

React Library Doesn’t Work

Symptom: React library throws errors Solution: Check if compat is configured:
If still broken, the library may use React internals. Find a Preact alternative.

onChange Not Firing

Cause: Preact uses onInput for real-time updates Solution:

DevTools Not Working

Solution: Install Preact DevTools (not React DevTools):

Bundle Size Too Large

Check:
Solutions:
  1. Remove unused dependencies
  2. Use dynamic imports for routes
  3. Check for duplicate React/Preact
  4. Use Signals instead of heavy state libraries

When to Choose Preact

Choose Preact When:

βœ… Bundle size is critical (mobile, emerging markets) βœ… You want React DX with better performance βœ… Building performance-critical applications βœ… You need fine-grained reactivity (Signals) βœ… Migrating from React but want smaller bundle βœ… Every kilobyte counts βœ… You value simplicity and speed

Choose React When:

βœ… You need the full React ecosystem βœ… Using React-specific libraries βœ… Team is deeply invested in React βœ… Bundle size doesn’t matter βœ… Need bleeding-edge React features first

Choose Something Else When:

  • Need SSR/SSG: Use Next.js, Nuxt, or SvelteKit
  • Want full framework: Use Nuxt, Next.js, or Angular
  • Hate JSX: Use Vue or Svelte
  • Want even smaller: Use vanilla JS or Alpine.js

Next Steps

React Guide

Compare with full React setup

Signals Documentation

Deep dive into Signals

Preact Router

Routing documentation

preact/compat

React compatibility guide