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