
Fresh: Deno framework s islands architekturou
Porovnání Fresh frameworku s React/Next.js a Svelte/SvelteKit - build proces, runtime, reaktivita a šablony.
Fresh je webový framework postavený na Deno, který přináší zajímavý přístup k tvorbě webových aplikací. Na rozdíl od tradičních frameworků jako Next.js nebo SvelteKit používá tzv. islands architekturu a nemá žádný build krok.
Co je Fresh
Fresh je framework pro server-side rendering s minimálním JavaScriptem na klientu. Stránky se renderují na serveru jako Preact komponenty a ve výchozím stavu se do prohlížeče neposílá žádný JavaScript.
Interaktivita se přidává pouze tam, kde je potřeba – pomocí tzv. islands (ostrovů). Zbytek stránky zůstává jako statické HTML.
Co je Preact
Fresh používá Preact – odlehčenou alternativu k Reactu s téměř identickým API. Hlavní rozdíl je velikost: Preact má pouze 3 KB, zatímco React ~40 KB.
| Preact | React | |
|---|---|---|
| Velikost | 3 KB | ~40 KB |
| Virtual DOM | Ano | Ano |
| Hooks | Ano | Ano |
| API kompatibilita | Téměř stejné | |
Pro Fresh je Preact ideální volba – minimální runtime pro islands, ale známá syntaxe pro React vývojáře. Většina React kódu funguje v Preact s aliasem preact/compat.
Islands architektura
Hlavní myšlenka islands architektury je jednoduchá: většina webových stránek je statická. Interaktivní jsou jen některé části – formuláře, menu, košík v e-shopu.
Proč tedy hydratovat celou stránku JavaScriptem, když interaktivní je jen malá část?
// Fresh – pouze CartWidget dostane JavaScript
export default function ProductPage() {
return (
<div>
<Header /> {/* Statické HTML */}
<ProductGallery /> {/* Statické HTML */}
<CartWidget /> {/* Island: ~15 KB JS */}
<Footer /> {/* Statické HTML */}
</div>
);
}
Srovnejme s tradičním přístupem v Next.js:
// Next.js – celá stránka se hydratuje (~150 KB JS)
export default function ProductPage() {
return (
<div>
<Header />
<ProductGallery />
<CartWidget />
<Footer />
</div>
);
}
Rozdíl může být až 10× v množství JavaScriptu poslaného do prohlížeče.
Partials: Client-side navigace
Od verse 1.5 Fresh podporuje client-side navigaci bez full page reload pomocí tzv. Partials. Stačí přidat atribut f-client-nav a označit měnící se části stránky:
// routes/_app.tsx
export default function App({ Component }) {
return (
<html>
<body f-client-nav> {/* Zapne client-side navigaci */}
<Header />
<Partial name="main-content">
<Component /> {/* Pouze tato část se mění */}
</Partial>
<Footer />
</body>
</html>
);
}
Výhody Partials:
- SPA-like navigace – stránky se mění bez reloadu
- Zachování stavu – islands si pamatují svůj stav při navigaci
- Optimalisace – s atributem
f-partialserver vrací pouze potřebné části
<!-- Optimalisovaný link – fetchuje pouze partial -->
<a href="/produkty" f-partial="/partials/produkty">
Produkty
</a>
Sdílení stavu mezi ostrovy
Ostrovy jsou isolované Preact aplikace, ale stav mezi nimi lze sdílet pomocí exportovaných signálů:
// utils/cart.ts
import { signal } from "@preact/signals";
export const cart = signal<string[]>([]);
// islands/AddToCart.tsx
import { cart } from "../utils/cart.ts";
export default function AddToCart({ product }) {
return (
<button onClick={() => cart.value = [...cart.value, product]}>
Přidat do košíku
</button>
);
}
// islands/Cart.tsx
import { cart } from "../utils/cart.ts";
export default function Cart() {
return <span>Položek: {cart.value.length}</span>;
}
Oba ostrovy importují stejný signál → automatická synchronisace. Důležité rozdíly:
| Přístup | Použití |
|---|---|
useSignal() |
Lokální stav (každý island má svůj) |
Exportovaný signal() |
Sdílený stav mezi ostrovy |
Omezení: useContext nefunguje napříč ostrovy (context existuje jen na serveru) a funkce nelze předávat jako props.
Build a runtime na produkci
Zde je zásadní rozdíl mezi Fresh a ostatními frameworky.
Fresh: Zero build
Fresh nemá build krok. Kód se kompiluje za běhu přímo v Deno. To znamená:
- Žádný webpack, Vite, esbuild
- Žádné
node_modules– Deno stahuje závislosti z URL nebo pomocínpm:prefixu (např.import _ from "npm:lodash") a cachuje je globálně - Žádný
distnebo.nextadresář - TypeScript funguje nativně bez konfigurace
Spuštění projektu:
deno run -A main.ts
Na produkci běží stejný kód jako při vývoji. Každý request renderuje stránku just-in-time.
Next.js: Tradiční build
Next.js vyžaduje klasický build proces:
npm run build # Kompilace, bundling, optimalisace
npm run start # Spuštění produkčního serveru
Výstupem je adresář .next s optimalisovanými assety. Na produkci běží předkompilovaný kód.
SvelteKit: Kompilace jako výhoda
SvelteKit má také build krok, ale s důležitým rozdílem – Svelte je kompilátor:
npm run build # Svelte kompiluje komponenty na vanilla JS
npm run preview # Náhled produkčního buildu
Svelte kompilace odstraní framework runtime z výsledného kódu. Proto má SvelteKit typicky o 50% menší bundle než React/Next.js.
Srovnání build procesu
| Framework | Build krok | Výstup na produkci |
|---|---|---|
| Fresh | Žádný | Zdrojový kód + Deno runtime |
| Next.js | Turbopack (od v16 výchozí) | Optimalisované bundly + Node.js |
| SvelteKit | Vite + Svelte compiler | Vanilla JS + adapter (Node/Edge) |
Tailwind CSS a AOT build
Fresh od verse 1.6 má oficiální Tailwind CSS plugin. Ten ale vyžaduje AOT (Ahead-of-Time) build – jednu z výjimek z jinak zero-build filozofie (další je např. předkompilace islands pro produkci):
# Fresh bez Tailwind
deno run -A main.ts # Spustí se rovnou
# Fresh s Tailwind
deno task build # Vygeneruje CSS + předkompiluje islands
deno run -A main.ts # Pak spustí server
Konfigurace:
// fresh.config.ts
import tailwind from "$fresh/plugins/tailwind.ts";
export default defineConfig({
plugins: [tailwind()],
});
Proč AOT? Tailwind skenuje zdrojové soubory a generuje CSS pouze pro použité třídy. To nelze efektivně dělat za běhu.
| Twind (starý) | Tailwind (nový) | |
|---|---|---|
| Build | Žádný | AOT (deno task build) |
| CSS generování | Runtime (per-request) | Předgenerované |
| Výkon | Pomalejší | Rychlejší |
| Editor IntelliSense | Omezený | Plná podpora |
Poznámka: Pro VS Code IntelliSense je potřeba nastavit "nodeModulesDir": "manual" v deno.json.
Rychlost a výkon
Velikost bundlu
Díky islands architektuře Fresh posílá 60–80% méně JavaScriptu než Next.js. SvelteKit je někde mezi – menší bundle než React díky kompilaci, ale ve výchozím stavu hydratuje celou stránku (partial hydration vyžaduje komunitní plugin jako sveltekit-is-land).
| Framework | Typický JS bundle | Poznámka |
|---|---|---|
| Fresh | 15–30 KB | Pouze interaktivní ostrovy |
| SvelteKit | 50–80 KB | Žádný runtime, kompilovaný kód |
| Next.js | 100–200 KB | React runtime + komponenty |
Time to Interactive (TTI)
Fresh dosahuje nejrychlejší interaktivity, protože prohlížeč zpracovává minimum JavaScriptu. SvelteKit je druhý díky menšímu bundlu. Next.js je nejpomalejší kvůli velikosti React runtime.
Time to First Byte (TTFB)
Zde je situace složitější:
- Fresh – každý request renderuje stránku znovu (žádná cache ve výchozím stavu)
- Next.js – nabízí SSG, ISR, edge caching
- SvelteKit – podobné možnosti jako Next.js
Pro statické stránky může být Next.js/SvelteKit rychlejší díky předrenderování. Fresh ale může použít edge deployment na Deno Deploy.
Rozdíly v reaktivitě
Fresh: Preact Signals
Fresh používá Preact Signals pro reaktivitu v islands:
import { signal } from "@preact/signals";
// Vytvoření reaktivní hodnoty
const count = signal(0);
export default function Counter() {
return (
<div>
<p>Počet: {count}</p>
<button onClick={() => count.value++}>+</button>
</div>
);
}
Signals jsou jemnozrnné – při změně se překreslí jen ta část DOM, která signál používá, ne celá komponenta.
Svelte: Runes (Svelte 5)
Svelte 5 přináší runes – nový systém reaktivity:
<script>
let count = $state(0);
function increment() {
count++;
}
</script>
<p>Počet: {count}</p>
<button onclick={increment}>+</button>
Svelte kompiluje reaktivitu do vanilla JS. Žádný runtime, změny jsou sledované kompilátorem.
React/Next.js: Hooks
React používá hooks a virtuální DOM:
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Počet: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
Při každé změně stavu se překreslí celá komponenta a React porovná virtuální DOM s reálným.
Srovnání reaktivity
| Framework | Mechanismus | Granularita |
|---|---|---|
| Fresh (Signals) | Reaktivita na úrovni hodnot | Pouze změněné DOM uzly |
| Svelte (Runes) | Kompilovaná reaktivita | Pouze změněné DOM uzly |
| React (Hooks) | Virtual DOM diffing | Celá komponenta + děti |
Rozdíly v šablonách
Fresh: JSX/TSX
Fresh používá standardní JSX syntaxi (Preact je API kompatibilní s Reactem):
export default function Article({ title, content }) {
return (
<article>
<h1>{title}</h1>
<div dangerouslySetInnerHTML={{ __html: content }} />
</article>
);
}
Výhody: známá syntaxe pro React vývojáře, plná síla JavaScriptu/TypeScriptu.
Svelte: Vlastní syntaxe
Svelte má vlastní šablonovací syntaxi:
<script>
let { title, content } = $props();
</script>
<article>
<h1>{title}</h1>
<div>{@html content}</div>
</article>
Výhody: čistší syntaxe, méně boilerplate, HTML-first přístup. Validní HTML je zároveň validní Svelte šablona (JSX má drobné rozdíly – className místo class, htmlFor místo for).
Srovnání syntaxe
| Vlastnost | Fresh (JSX) | Svelte |
|---|---|---|
| Podmínky | {condition && <div/>} |
{#if condition}<div/>{/if} |
| Cykly | {items.map(i => <li>{i}</li>)} |
{#each items as i}<li>{i}</li>{/each} |
| CSS | CSS-in-JS nebo externí soubory | Scoped CSS přímo v komponentě |
| Událost | onClick={handler} |
onclick={handler} |
Ekosystém a adopce
Zde je Fresh v nevýhodě:
- Fresh – běží pouze na Deno, menší komunita, méně knihoven
- Next.js – obrovský ekosystém, tisíce balíčků, široká adopce
- SvelteKit – rostoucí komunita, dobrá dokumentace, npm kompatibilita
Kdy použít Fresh
Fresh je vhodný, pokud:
- Chcete minimální JavaScript na klientu
- Preferujete jednoduchost bez build konfigurace
- Používáte nebo chcete používat Deno
- Nasazujete na edge (Deno Deploy)
- Vytváříte obsahové weby s minimální interaktivitou
- Děláte rychlý iterativní vývoj (např. s AI) – absence buildu znamená okamžitou zpětnou vazbu bez čekání na kompilaci
Kdy Fresh nepoužít
Fresh není ideální pro:
- Projekty vyžadující specifické npm balíčky – Deno podporuje
npm:specifier, ale ne všechny balíčky fungují (zejména ty závislé na Node.js API) - Týmy zvyklé na Node.js workflow – Fresh běží pouze na Deno
- Aplikace s komplexním sdíleným stavem –
useContextnefunguje napříč ostrovy
Pro běžné interaktivní aplikace (e-shop, dashboard) je Fresh díky Partials a sdíleným signálům použitelný. Limitující je především menší ekosystém. Poznámka k npm závislostem: V dnešní době není typicky potřeba tolik externích balíčků. S pomocí AI nástrojů je často jednodušší nechat si vygenerovat řešení na míru než hledat, instalovat a udržovat závislosti.
Nasazení na Deno Deploy
Jednou z velkých výhod Fresh je snadné nasazení na Deno Deploy – edge hosting přímo od tvůrců Deno:
- Nasazení na pár kliknutí – propojení s GitHub repozitářem a automatický deploy při každém push
- Globální edge síť – kód běží blízko uživatelům po celém světě
- Žádná konfigurace – Fresh projekty fungují out-of-the-box
- Bezplatný tarif – 100 000 requestů denně zdarma
# Postup nasazení:
# 1. Push kódu na GitHub
# 2. Přihlášení na dash.deno.com
# 3. "New Project" → výběr repozitáře
# 4. Hotovo – aplikace běží na *.deno.dev doméně
Pro produkční nasazení stačí připojit vlastní doménu a nastavit environment variables. Žádný Docker, žádný CI/CD pipeline – vše je integrované.
Závěr
Fresh představuje zajímavou alternativu k tradičním meta-frameworkům. Jeho islands architektura a zero-build přístup jsou osvěžující v době, kdy se build konfigurace stávají stále složitějšími.
Pro obsahové weby a projekty s důrazem na výkon může být Fresh výbornou volbou. Pro komplexní aplikace s bohatou interaktivitou ale zůstává Next.js nebo SvelteKit praktičtější volbou díky většímu ekosystému.
Klíčové je vybrat nástroj podle potřeb projektu, ne podle hype.
Související články
Jak vkládat 3D objekty na web pomocí Three.js
Které formáty použít, jak vytvářet modely pomocí AI a kdy raději použít obrázek nebo video.
JavaScript null a undefined
Rozdíly mezi null a undefined v JavaScriptu, kdy je používat a jak se vyhnout běžným chybám.
Sleep v JavaScriptu
Jak implementovat sleep/delay funkcionalitu v JavaScriptu pomocí Promise a async/await