Qu’est-ce que Preact ?
Preact est une alternative miniature à React. Il implémente le même modèle de composants, les mêmes hooks et un DOM virtuel, le tout pour environ trois kilo-octets compressés (gzipped). Pour la plupart des composants, le code écrit pour React fonctionne sur Preact avec peu ou pas de modifications.
Cette taille est tout l’intérêt du projet. Lorsque vous intégrez un widget dans une page que vous ne contrôlez pas entièrement, que vous développez une interface où la performance est critique ou que vous souhaitez simplement limiter votre budget JavaScript, quelques kilo-octets de framework peuvent faire une différence mesurable. Preact vous offre un modèle familier sans l’encombrement.
Composants et hooks
Les composants Preact sont des fonctions qui retournent du JSX, et les hooks proviennent de preact/hooks.
// Search.jsx
import { useState } from "preact/hooks";
export function Search({ onSearch }) {
const [query, setQuery] = useState("");
return (
<form
onSubmit={(event) => {
event.preventDefault();
onSearch(query);
}}
>
<input
value={query}
onInput={(event) => setQuery(event.currentTarget.value)}
/>
</form>
);
}
useState, useEffect, useRef, useContext, useMemo et useCallback se comportent tous comme vous vous y attendez. Les principales différences sont mineures : Preact supporte à la fois class et className, et utilise des noms d’événements natifs tels que onInput et onDblClick.
Preact Signals
Les Signals sont la primitive de réactivité native de Preact. Un signal détient une valeur et, lorsqu’il est lu à l’intérieur du JSX, il abonne cette partie du DOM à ladite valeur. Modifier la valeur met à jour le DOM directement — aucun re-render du composant n’est nécessaire.
// Counter.jsx
import { signal, computed } from "@preact/signals";
const count = signal(0);
const double = computed(() => count.value * 2);
export function Counter() {
return (
<div>
<button onClick={() => count.value++}>
Count: {count}
</button>
<p>Double: {double}</p>
</div>
);
}
Comme les signals existent en dehors de l’arbre des composants, vous pouvez les partager et les muter de n’importe où, y compris depuis des modules simples ou des gestionnaires d’événements. C’est la méthode recommandée pour gérer un état qui change fréquemment.
La couche de compatibilité
Le package preact/compat mappe l’API de React vers Preact. Créez des alias pour react et react-dom dans votre bundler, et la plupart des bibliothèques React fonctionneront sans modification.
// vite.config.js
import { defineConfig } from "vite";
import preact from "@preact/preset-vite";
export default defineConfig({
plugins: [preact()],
resolve: {
alias: {
react: "preact/compat",
"react-dom": "preact/compat",
"react-dom/client": "preact/compat/client",
},
},
});
C’est ainsi que les projets Preact utilisent l’écosystème React — bibliothèques de composants, routage, gestionnaires d’état et plus encore — tout en conservant un runtime plus léger. Le compromis est un léger surcoût lié à la couche de compatibilité, mais cela reste bien inférieur à la taille de React lui-même.
Pas d’étape de build avec htm
Si vous souhaitez vous passer totalement de l’étape de build, la bibliothèque htm propose une alternative aux JSX basée sur les tagged templates.
// app.js
import { h, render } from "preact";
import htm from "htm";
const html = htm.bind(h);
function App({ name }) {
return html`<h1>Hello, ${name}!</h1>`;
}
render(html`<${App} name="Ada" />`, document.body);
C’est très pratique pour l’amélioration progressive (progressive enhancement), les démos rapides ou l’intégration d’une petite île interactive dans une page rendue côté serveur.
Différences avec React
Le chevauchement est important, mais quelques détails diffèrent :
- Preact utilise les noms d’événements DOM natifs, ainsi
onInputetonDblClickremplacent certains noms spécifiques à React. classetclassNamefonctionnent tous les deux, etforethtmlForsont tous deux acceptés.- Il n’y a pas de pooling d’événements synthétiques, car Preact n’a jamais utilisé de pooling pour les événements.
- Certaines fonctionnalités récentes de React, comme certaines parties de l’API des server-components, ne sont pas implémentées.
- Les types JSX de TypeScript proviennent de Preact et nécessitent un alias lors de l’utilisation de compat.
Pour les composants et hooks du quotidien, vous remarquerez rarement la différence.
Quand utiliser Preact
Preact est idéal lorsque la taille et le coût de démarrage sont prioritaires : widgets intégrables, pages marketing avec un peu d’interactivité, sites de contenu et applications avec des budgets de performance stricts. C’est également une cible de migration douce pour une base de code React existante qui doit être allégée.
Si vous dépendez d’outils spécifiques à React, de fonctionnalités très récentes ou d’une bibliothèque qui repose sur les mécanismes internes de React, rester sur React sera peut-être moins contraignant. La bonne nouvelle est que le chevauchement des API fait que cette décision n’est pas irréversible.
Bonnes pratiques
- Utilisez les signals pour les états qui changent fréquemment ou qui sont partagés en dehors de l’arbre.
- Ne vous tournez vers
preact/compatque lorsque vous avez besoin de packages de l’écosystème React. - Créez des alias pour les types React ainsi que pour les modules d’exécution lors de l’utilisation de la compatibilité.
- Gardez vos composants petits et évitez les re-renders inutiles en lisant les signals au point d’utilisation.
- Mesurez l’impact réel sur le bundle — les gains sont significatifs, mais c’est toujours votre propre code qui prédomine.
- Privilégiez
preact/preset-vitepour configurer correctement le build dès le départ.
Erreurs courantes
- Mélanger des packages React et Preact sans utiliser l’alias de compatibilité.
- Oublier d’aliaser
react-dom/client, ce qui rend l’API root obsolète. - Supposer que toutes les bibliothèques React fonctionnent sans tester la couche de compatibilité.
- Abuser des signals et re-render des composants qui pourraient lire un signal directement.
- Considérer Preact comme identique à React en termes de fonctionnalités et être surpris par un cas particulier.
- Ajouter Preact pour réduire la taille du bundle, puis importer une dépendance volumineuse qui annule le gain.
Et après ?
Preact est la réponse pragmatique lorsque le modèle de React est pertinent, mais que sa taille ne l’est pas. Comparez-le avec React et Svelte, couplez-le avec Vite, et gardez vos fondamentaux en JavaScript bien en tête. Essayez ensuite d’intégrer un petit widget Preact dans une page HTML simple pour constater à quel point son impact est minime.