Moderní tvorba webových aplikací
O webu

Fresh: Deno framework s islands architekturou

Porovnání Fresh frameworku s React/Next.js a Svelte/SvelteKit - build proces, runtime, reaktivita a šablony.

15 minut

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-partial server 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ý dist nebo .next adresář
  • 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"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 stavemuseContext nefunguje 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 architekturazero-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.

15 minut

Detekce otevření DevTools

Jak zjistit, že se na stránce otevřely vývojářské nástroje.

13 minut

JavaScript nullundefined

Rozdíly mezi nullundefined v JavaScriptu, kdy je používat a jak se vyhnout běžným chybám.

12 minut

Sleep v JavaScriptu

Jak implementovat sleep/delay funkcionalitu v JavaScriptu pomocí Promiseasync/await

6 minut

Novinky e-mailem

Když budu mít něco opravdu zajímavého, můžu vám to poslat e-mailem

Přidej se k 500+ čtenářům
Jen kvalitní obsah
Žádný spam

Web jecas.cz píše Bohumil Jahoda, kontakt
Seznam všech článků
2013–2026