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 - Usepreact/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
| Feature | Preact | React |
|---|---|---|
| Bundle Size | 3kB | ~45kB |
| Runtime Speed | β‘ Faster | β‘ Fast |
| API Compatibility | ~95% with compat | 100% |
| Hooks | β Yes | β Yes |
| Fragments | β Yes | β Yes |
| Context | β Yes | β Yes |
| Signals | β Unique feature | β No |
| DevTools | β Yes | β Yes |
| Ecosystem | β οΈ Smaller | β Huge |
| Learning Curve | β Easy (if you know React) | β οΈ Moderate |
| TypeScript | β Full support | β Full support |
| Best for | Performance-critical apps | Large ecosystems |
Bundle Size Comparison
Real-world bundle sizes (production, gzipped):- 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:http://localhost:3000 to see your app!
Project Structure
Configuration
Vite Configuration
Configure Vite for Preact with React compatibility:frontend/vite.config.js
- 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 usesh function instead of React.createElement:
@preact/preset-vite, you donβt need to import h manually.
3. Event Naming
Preact uses standard DOM event names:onInput for real-time updates, while React uses onChange.
4. Class Names
Both supportclassName, 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
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
- No dependency arrays
- No useMemo/useCallback needed
- Share state without Context API
- Signals add only ~1.6kB to your bundle
Basic Signals
- Access/update with
.valuein JavaScript - Use signal directly in JSX (no
.valueneeded) - Changes trigger automatic, fine-grained updates
Computed Signals
Derived values that automatically update:- 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:Signals vs Hooks
| Feature | Signals | Hooks (useState) |
|---|---|---|
| Bundle Size | +1.6kB | Included |
| Performance | β‘ Fastest | β‘ Fast |
| Re-renders | β No | β Yes |
| Global State | β Easy | β οΈ Needs Context |
| Computed Values | β Built-in | β οΈ useMemo |
| Learning Curve | β Simple | β Familiar |
| Best for | Shared state, counters, filters | Local component state |
- 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:Option 1: preact-router (Recommended)
Lightweight routing made for Preact:Option 2: React Router (with compat)
Use React Router withpreact/compat:
Option 3: wouter (Minimalist)
Tiny routing library:React Compatibility (preact/compat)
Run most React libraries unchanged withpreact/compat.
Setup
Already configured in the Vite config above. Just install React libraries: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, emotionWhat 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:Styling
CSS Modules
Tailwind CSS
Install Tailwind:tailwind.config.js:
Styled Components
Development Workflow
Starting Development
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
- Builds Preact app (
npm run build) - Builds Go binary with embedded frontend
Build Optimizations
The Preact template includes optimizations: Code splitting: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: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
- Total JS: <20kB (with routing)
- Main bundle: <15kB
Troubleshooting
h is not defined
Error:
@preact/preset-vite
Solution: Install preset:
vite.config.js:
React Library Doesnβt Work
Symptom: React library throws errors Solution: Check if compat is configured:onChange Not Firing
Cause: Preact usesonInput for real-time updates
Solution:
DevTools Not Working
Solution: Install Preact DevTools (not React DevTools):Bundle Size Too Large
Check:- Remove unused dependencies
- Use dynamic imports for routes
- Check for duplicate React/Preact
- 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 speedChoose 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 firstChoose 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