Preact — библиотека интерфейсов с API, знакомым по React, но с компактным ядром и ставкой на производительность. В сентябре 2026 года вышла Preact 11: она сохранила привычные компоненты и хуки, добавила Hydration 2.0, автоматическую передачу ref и перешла на ESM. Для сайта, где важен размер бандла и скорость отрисовки, это практичная альтернатива тяжёлому фреймворку — если понимать отличия и не тащить compat без необходимости.
Что нового в Preact 11
Одиннадцатая версия — обновление с минимумом ломающих изменений после Preact X:
- Hydration 2.0: асинхронные границы могут возвращать ноль или несколько DOM-узлов;
- ref передаётся как обычный prop,
forwardRefбольше не нужен; - хуки сравнивают зависимости через
Object.is, включаяNaN; createPortalдоступен из ядраpreact;- пакеты распространяются только как ESM с расширением
.mjs; - в
preact/compatпоявилисьuse()иuseEffectEvent(); SuspenseListудалён, аuseRefв типах требует начальное значение.
Если проект на Preact 10, обновите TypeScript до 5.1 и проверьте сборку: CommonJS- и UMD-точки входа больше не поставляются. Обычно миграция сводится к правке импортов и типов.
Быстрый старт
Генератор create-preact поднимает проект на Vite и предлагает TypeScript, маршрутизацию через preact-iso и ESLint. Конфигурацию можно менять позже.
npm init preact
cd my-preact-app
npm install
npm run dev
npm run build
В разработке Vite отдаёт модули без минификации, а npm run build собирает оптимизированный dist/. Проверить production-сборку можно командой npm run preview. Не публикуйте dist без проверки путей, если приложение в подкаталоге.
Компоненты и хуки
Компонент — функция, принимающая props и возвращающая JSX. Хуки импортируются из preact/hooks, а не из react. Состояние обновляйте функцией, чтобы не терять предыдущее значение при быстрых кликах.
import { useState, useEffect } from 'preact/hooks';
export function Counter({ step = 1 }) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Счёт: ${count}`;
}, [count]);
return (
<button onClick={() => setCount((c) => c + step)}>
Счёт: {count}
</button>
);
}
Монтирование выполняет render из ядра. В Preact 11 аргумент replaceNode удалён, для замены части DOM используйте preact-root-fragment. В TypeScript настройте JSX на Preact, иначе редактор будет искать типы React.
import { render } from 'preact';
import { Counter } from './counter';
const root = document.getElementById('app');
if (root) render(<Counter step={2} />, root);
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact",
"strict": true,
"skipLibCheck": true,
"moduleResolution": "bundler",
"target": "ES2022"
}
}
skipLibCheck полезен, когда сторонняя библиотека приносит типы React: Preact compat покрывает не всё, и без этого флага сборка может падать на чужих .d.ts. Но не отключайте strict — типы здесь помогают ловить ошибки в props и ref.
Signals
Signals хранят состояние вне компонента и обновляют только узлы, читающие значение. Это не замена хукам, а дополнение для часто меняющихся данных: счётчиков, фильтров, корзины.
import { signal, computed, effect } from '@preact/signals';
const count = signal(0);
const double = computed(() => count.value * 2);
effect(() => {
console.log(double.value);
});
count.value++;
В компоненте достаточно прочитать count.value в JSX — подписка произойдёт автоматически. Для локального состояния есть useSignal и useComputed. Не превращайте все данные в глобальные сигналы: это усложняет тесты и жизненный цикл.
Совместимость с React
preact/compat позволяет использовать React-библиотеки: компоненты, хуки, порталы, lazy и Suspense. Vite-пресет подключает алиасы сам, в существующей сборке их пропишите вручную.
resolve: {
alias: {
react: 'preact/compat',
'react-dom': 'preact/compat',
'react-dom/test-utils': 'preact/test-utils',
'react/jsx-runtime': 'preact/jsx-runtime'
}
}
Compat не гарантирует побайтовую совместимость: библиотеки, которые лезут во внутренности React или полагаются на SuspenseList, могут не работать. В Preact 11 px для числовых style-значений и defaultProps перенесены в compat, а ref для классовых компонентов не передаётся — как в React. Проверяйте такие места тестами.
Маршрутизация и SSR
preact-iso — лёгкий роутер от команды Preact. Он поддерживает ленивую загрузку страниц и SSR. Для метаданных и кода ответа используйте его возможности, а не самодельные обходы.
import { LocationProvider, Router, Route } from 'preact-iso';
import { Home } from './pages/home';
import { Catalog } from './pages/catalog';
export function App() {
return (
<LocationProvider>
<Router>
<Route path="/" component={Home} />
<Route path="/catalog" component={Catalog} />
</Router>
</LocationProvider>
);
}
Для серверного рендеринга используйте preact-render-to-string, на клиенте — hydrate. В Preact 11 гидратация лучше работает с потоковым HTML и асинхронными границами, но разметка должна совпадать. Если сервер и клиент отдают разный HTML, гидратация будет нестабильной.
import renderToString from 'preact-render-to-string';
import { App } from './app';
const html = renderToString(<App />);
console.log(html);
Сборка и производительность
Размер бандла — это время загрузки. После сборки посмотрите, сколько занимает JS и сколько из него приходится на compat. Если React-библиотеки не нужны, compat не подключайте: он добавляет код и компромиссы.
npm run build
du -h dist/assets/*.js
gzip -c dist/assets/index-*.js | wc -c
npm run preview
Делите код по маршрутам через lazy и динамические импорты, не грузите тяжёлые виджеты на первом экране, используйте preact/debug только в разработке. Для профилирования подойдут React DevTools. Следите за Core Web Vitals, а не только за размером файла: длинный main-thread портит впечатление даже при маленьком бандле.
Тесты и качество
Для тестов хватает Vitest и @testing-library/preact. Проверяйте поведение, а не внутреннюю структуру компонентов: так тесты переживут рефакторинг и переход на сигналы.
npm i -D vitest jsdom @testing-library/preact
npx vitest run
npm run build
В тестах проверяйте доступность: роль кнопки, подпись поля, порядок фокуса. Preact не навязывает разметку, семантика остаётся ответственностью разработчика. dangerouslySetInnerHTML используйте только после санитизации: библиотека экранирует значения по умолчанию, но не отменяет правила безопасности.
Итог
Preact 11 — практичный выбор для интерфейсов, где важны размер, скорость и знакомый API. Он даёт хуки, сигналы, SSR, роутер и совместимость с React-экосистемой, но требует дисциплины: не подключать compat без нужды, следить за типами и проверять поведение сторонних библиотек. Начните с npm init preact, соберите маленький экран на хуках, затем добавьте сигналы и измерьте бандл. Если после этого приложение остаётся быстрым и понятным, вы получили именно то, за чем идут к Preact — лёгкий интерфейс без лишнего веса.