« Zéro JavaScript par défaut », c'est l'argument de Fresh depuis le premier jour. La vraie petite ligne en bas du contrat : même une page sans island ni partial embarquait un petit entry client, entre 14 et 22 KB gzippés selon le projet. Pas un scandale, mais pas zéro non plus. Fresh 2.3, annoncé sur le blog Deno, comble cet écart pour de bon.
Vraiment zéro, cette fois
Rien à configurer. Après la mise à jour, Fresh vérifie au rendu si une page utilise réellement des islands ou des partials. Si ce n'est pas le cas, la réponse ne contient aucune balise script, aucun header de préchargement de module, aucun bundle client. Une page marketing, un article de blog, une page de docs : du HTML brut sur le réseau.
Pour le constater vous-même, mettez à jour et ouvrez l'onglet réseau sur une route statique. La différence se mesure facilement : là où la 2.2 chargeait l'entry client sur chaque page, la 2.3 ne charge rien tant qu'aucune island n'apparaît.
View Transitions en un attribut
Fresh proposait déjà la navigation côté client via son système de partials (f-client-nav). La version 2.3 y branche l'API View Transitions du navigateur. On active avec un attribut, juste à côté de celui qu'on a déjà :
<body f-client-nav f-view-transition>
Les navigations partielles sont désormais enveloppées dans document.startViewTransition(), et l'animation se règle en CSS pur :
::view-transition-old(root) {
animation: fade-out 150ms ease-out;
}
::view-transition-new(root) {
animation: fade-in 150ms ease-in;
}
Chrome 111+, Edge 111+ et Safari 18+ animent nativement. Firefox pas encore, il retombe simplement sur une mise à jour partielle classique, donc activer l'option ne coûte rien.
WebSockets sans serveur d'appoint
Jusqu'ici, le temps réel dans une app Fresh passait soit par Deno.upgradeWebSocket câblé à la main, soit par un serveur séparé. Fresh 2.3 fait des WebSockets un citoyen de première classe. La version la plus rapide vit sur l'instance App :
app.ws("/ws", {
open(ctx) {
console.log("client connecté");
},
message(ctx, event) {
ctx.send(`echo : ${event.data}`);
},
close(ctx) {
console.log("client parti");
},
});
Les routes basées sur des fichiers obtiennent la même capacité via ctx.upgrade() dans un handler GET. Et si vous devez gérer les connexions vous-même, un salon de chat qui garde un Set de sockets, par exemple, appeler ctx.upgrade() sans objet de handlers retourne le WebSocket brut et vous laisse piloter le cycle de vie.
Le reste de la release
Trois ajouts plus discrets méritent l'attention :
- Nonces CSP. Fresh peut injecter un nonce par requête dans les scripts et styles qu'il émet, ce qui rend une
Content-Security-Policystricte praticable sans maintenir des hashes à la main. - Temporal dans les islands. Les huit types Temporal passent comme props d'island et se sérialisent correctement entre serveur et client, utile maintenant que Temporal se stabilise dans les runtimes.
- Prerendering. Marquez une route avec
prerender: trueet Fresh la rend en HTML statique au build ; les routes dynamiques peuvent énumérer leurs chemins. Combiné aux pages zéro-JS, Fresh devient discrètement un très bon générateur de sites statiques.
Fresh 2.3 s'installe avec un simple deno update, et avec Deno 2.7+ les nouveaux projets se créent via deno create. Pour un framework dont l'identité entière est la retenue, c'est la release où la retenue cesse d'être approximative.



