Fresh(フレッシュ)は、Denoの開発チームが生んだフルスタックのWebフレームワークです。ページはサーバーでHTMLに描画し、ボタンやフォームなど動かす部分(島)のJavaScriptだけをブラウザへ送ります。最新の安定版は2026年4月28日公開の2.3.3で、2.0からはViteプラグインとして動く構成に変わりました。2026年には開発体制も大きく動いています。6月にDeno社から独立したGitHub組織へ移り、10月9日にはDenoチーム全体のCloudflare合流と、Deno Deployの終了が発表されました。この記事では、Freshの仕組みと書き方を2.3.3の実物で確かめ、配信されるJSの量を測ったうえで、2026年10月時点で採用してよい条件を整理します。
まとめ:Fresh 2.3の配信JSとDeno開発終了予定を踏まえた採用判断
- Freshは「サーバー描画+島だけhydrate」のアイランドアーキテクチャを採るフレームワークで、UIはPreactで書きます。
- 最新安定版は2.3.3(2026年4月28日)です。2.0.0は2025年9月9日に正式リリースされ、リリースノートで「Viteプラグインになった」と明記しています。Viteなしで動かす旧Builderはレガシー扱いです。
- 2.3.3の雛形で測ると、島を置かないページの<script>は0件でした。島を1つ置くと3ファイル・計46,518バイト(gzip換算で約18.3KB)を読み込みます。
- Denoチームは2026年10月9日にCloudflareへの合流を発表しました。Deno Deployは6か月の運用ののち終了し、Denoランタイムの開発は1年間の保守リリースのあと終わります。
- Freshは独立組織freshframeworkへ移り、次のFresh 3(アルファ)はNode.jsなどDeno以外でも動く設計に作り直されています。
- 判断:既存のFreshアプリは2.3へ上げ、デプロイ先をDeno Deploy以外へ移します。1年以上運用する新規案件ではAstroなどを優先します。HonoXはアルファ段階の破壊的変更を許容できる場合に限って検討し、Fresh 3は正式版の公開後に評価します。
Deno Freshの定義と2026年10月時点の開発体制
公式ドキュメントはFreshを「Web標準の上に作られた、小さく速く拡張しやすいフルスタックWebフレームワーク」と説明しています(Fresh公式ドキュメント)。ルーティング、サーバー描画、データ取得、フォーム処理、ミドルウェアをひととおり備え、TypeScriptとJSXをDenoでそのまま扱えます。公式サイトは2026年6月にfresh.deno.devからusefresh.devへ移りました。
Fresh 2.3.3が安定版、Fresh 3はアルファ段階
版の流れは次のとおりです。日付はGitHub Releasesとnpmの公開日時(UTC)で確かめました。
| 版 | 公開日 | 位置づけ |
|---|---|---|
| 1.7.3 | 1系の最終版 | Viteなし・fresh.gen.ts方式 |
| 2.0.0 | 2025年9月9日 | 正式版・Viteプラグイン化 |
| 2.2.0 | 2025年11月18日 | マイナー更新 |
| 2.3.3 | 2026年4月28日 | 最新の安定版 |
| 3.0.0-alpha.202610031718 | 2026年10月8日 | npmの@frsh/freshで配布 |
2.x系の一覧はfreshframework/freshのReleases、JSRの@fresh/coreでも最新は2.3.3です。2.3.3のあと半年間、2系の新しい版は出ていません。
Deno社から独立したfreshframework組織
2026年6月3日、メンテナーのLuca Casonato氏がGitHub Discussionsの告知で、FreshをDenoとは別のGitHub組織へ移したと発表しました。同じ告知で、本人とMarvin Hagemeister氏がDeno社を離れたこと、今後のFreshは「Vite-only」になること、Nitroを使ってNode.jsやCloudflare WorkersなどDeno以外にも配備できるようにする計画が示されています。旧リポジトリgithub.com/denoland/freshは、現在freshframework/freshへ転送されます。
アイランドアーキテクチャの仕組み:既定はJSゼロ、島だけhydrate
Freshはすべてのページをサーバーで描画し、クライアントのJavaScriptを既定では送りません。対話が必要な部品だけをislands/フォルダ(またはroutes/内の(_islands)フォルダ)に置くと、その部品だけがブラウザでhydrate(イベントを結びつけて動かすこと)されます。ページ全体をhydrateするSPAとは逆に、動かす箇所を足していく考え方です。サーバー描画とクライアント描画の判断軸はCSRとSSRの違いと使い分けで、アイランドという設計そのものとAstroでの実装はアイランドアーキテクチャとは?仕組みとAstro 7の実装・JS量の実測、PPRとの違いで扱っています。
// islands/Counter.tsx(init が生成する島から装飾と型宣言を簡略化)
import type { Signal } from "@preact/signals";
import { Button } from "../components/Button.tsx";
export default function Counter(props: { count: Signal<number> }) {
return (
<div>
<Button onClick={() => props.count.value -= 1}>-1</Button>
<p>{props.count}</p>
<Button onClick={() => props.count.value += 1}>+1</Button>
</div>
);
}
島に渡せるpropsと渡せない値
島のpropsはサーバーでシリアライズしてHTMLに埋め込み、ブラウザで復元します。そのため渡せる値が決まっています(Serialization)。
- 渡せる:文字列・数値・真偽値・bigint・null・undefined、配列、プレーンオブジェクト、Date、URL、RegExp、Map、Set、Uint8Array、Temporalの各型、Preactのシグナル、サーバーで描画したJSX
- 渡せない:関数とクロージャ、クラスのインスタンス、Symbol、WeakMap・WeakSet、Promiseやストリーム
つまずきやすいのは、onClick={() => ...}のようなイベントハンドラーを親ページから島へ渡す書き方です。ハンドラーは島の中で定義します。同じシグナルを複数の島へ渡すと、ブラウザ側でも同じインスタンスになり、島どうしで状態を共有できます。ブラウザ専用のAPIを使う島は、fresh/runtimeのIS_BROWSERでサーバー描画時の実行を避けます。
Fresh 2.3.3の配信JSを実測:島なしページは0件、島1つで3ファイル
「既定はJSゼロ」が実際にどうなるかを、2026年10月28日にmacOS上のDeno 2.9.8で確かめました。deno run -Ar jsr:@fresh/initで生成した雛形(Fresh 2.3.3・Preact 10.29.8・Vite 7.3.7)に島を持たない/aboutを足し、deno task buildのあとdeno serveで本番ビルドを起動してHTMLとアセットを取得しています。
| ページ・ファイル | サイズ | gzip |
|---|---|---|
| /about(島なし)の<script> | 0件 | – |
| /(Counter島1つ)の<script> | 1件 | – |
| client-entry(Fresh実行部) | 34,669B | 13,089B |
| preact.module | 11,031B | 4,662B |
| fresh-island__Counter | 818B | 531B |
| 島1つの合計 | 46,518B | 約18.3KB |
島なしのページは、CSS 1ファイル(1,825B)以外に何も読み込みませんでした。島を1つ置くと、HTML末尾のモジュールスクリプトがboot({Counter}, …)を呼び、シリアライズ済みのprops(シグナルの初期値3)を渡します。重いのは島本体(818B)ではなく、Fresh実行部とPreactの計約45KBです。島を置いたページでは、この2ファイルが島本体とは別に読み込まれると見積もっておきます。実際の転送量は依存関係、ビルド設定、圧縮、ブラウザキャッシュで変わります。
もう1点、deno serveで起動した本番ビルドは、Accept-Encoding: gzipを付けて要求してもContent-Encodingを付けずに返しました。アセットにはcache-control: public, max-age=31536000, immutableが付きます。圧縮は前段のCDNやリバースプロキシで掛ける前提で構成します。
Fresh 2のUI実装とPreact 10系の依存関係
FreshのビューはPreactで書きます。JSX、props、フックの書き方はReactとほぼ同じで、状態管理には@preact/signalsを使います。Preact公式サイトは本体のサイズを3kBと表記しています(Preact)。
版には注意が要ります。Preact本体は2026年9月30日に11.0.0を出し、10月27日の11.1.0が最新です。一方、Fresh 2.3.3の雛形はnpm:preact@^10.29.1を指定しており、入るのは10系(今回は10.29.8)です。Preact 11へ上げたのはFresh 3の開発ブランチで、10月3日のコミットで依存を更新しています。Fresh 2のアプリでPreact 11専用の機能を前提にしないでください。
ReactとPreactの違い(互換性とバンドルサイズ)
React向けのライブラリはpreact/compat経由で動かします。Fresh 2のViteプラグインはreactやreact-domをpreact/compatへ自動で割り当てるため、npmのパッケージを通常どおり入れられます(Fresh 2.0ベータの公式記事)。ただしReactの内部実装に依存するライブラリは動かないことがあるので、主要な依存は採用前に動作を確かめます。Preact単体の定義、10系と11系の違い、compatでReact資産を移す手順はPreactとは?Reactとの違い・導入手順・compat移行と採用判断で整理しています。
Fresh 2のプロジェクト構成とルーティングの書き方
新規プロジェクトはdeno run -Ar jsr:@fresh/initで作ります。2.3.3のinitは、Tailwind・VS Code設定・Dockerの追加を--tailwind・--vscode・--dockerで選べます。生成されたファイルは次のとおりでした。
components/Button.tsx
islands/Counter.tsx # 島(ブラウザでも動く部品)
routes/_app.tsx # <html> の外枠
routes/index.tsx # /
routes/api/[name].tsx # /api/:name
static/ # 画像・CSSなど
client.ts # 全ページで読み込むクライアント入口
main.ts # サーバー入口(new App())
utils.ts # define ヘルパー
vite.config.ts # plugins: [fresh()]
deno.json
Denoのインストール、権限フラグ、LTSの選び方を含む環境づくりはDenoとFreshの開発環境セットアップで手順を追えます。
new App()とfsRoutes()で組むサーバー側の入口
Fresh 2では、main.tsでnew App()を作り、.use()でミドルウェア、.get()でルートを登録し、.fsRoutes()でファイルベースのルートを差し込みます(App)。ミドルウェアはctxを1つ受け取り、ctx.next()で次へ渡します。雛形のmain.tsを短くすると次のとおりです。
import { App, staticFiles } from "fresh";
import { type State } from "./utils.ts";
export const app = new App<State>();
app.use(staticFiles());
// ミドルウェアは ctx を1つ受け取る
app.use(async (ctx) => {
ctx.state.shared = "hello";
return await ctx.next();
});
app.get("/api2/:name", (ctx) => new Response(`Hello, ${ctx.params.name}!`));
// routes/ 配下のファイルをルートとして差し込む
app.fsRoutes();
routes/とislands/のファイル配置とURLの対応
routes/のファイルパスがそのままURLになります。routes/about.tsxは/about、routes/blog/[slug].tsxは/blog/:slug、(folder)はURLに出ないグループです。ページはutils.tsのdefine.page()で書くと型が付きます。
// routes/about.tsx(島を持たないページ=JSは送られない)
import { define } from "@/utils.ts";
export default define.page(() => {
return (
<main>
<h1>About</h1>
<p>This is the about page.</p>
</main>
);
});
Viteプラグイン化で変わった開発とビルドの流れ
Fresh 1.xは「ビルド不要」が売りでした。2.0の途中で方針が変わっています。2025年9月2日のベータ告知は、Viteを任意で使え、Viteなしでも使えると書いていました(Fresh 2.0 Graduates to Beta)。ところが9月9日の2.0.0のリリースノートは「Fresh is officially a vite plugin now」と明記し、ドキュメントもViteを使わないBuilderを「Viteプラグインが出る前のアルファ期に使っていたもの」と位置づけています。2.3.3のinitにはBuilderで生成する--builderが残っていますが、6月の告知はViteなしのFreshをやめる意向を示しているので、新しく作るならVite構成を選びます。
| 作業 | deno.json のタスク | 中身 |
|---|---|---|
| 開発サーバー | deno task dev | vite(島はHMR対応) |
| 本番ビルド | deno task build | vite build → _fresh/ |
| 本番起動 | deno task start | deno serve -A _fresh/server.js |
| Freshの更新 | deno task update | jsr:@fresh/update |
公式トップは今も「設定ファイルもビルドもnode_modulesも要らない」と書いています。ただ、それはファイル1つでサーバーを書く場合の話です。initの雛形ではnode_modulesが作られ(今回75MB)、本番前にvite buildが要ります(今回18秒)。_fresh/はGitに入れません。見返りとして、ルートを必要時に読み込む方式とDenoのJSX precompileで起動が速くなりました。ベータ告知によると、Fresh公式サイトの起動時間は86msから8msへ短くなっています。
Fresh 1.xから2への移行で書き換える箇所
1.xのプロジェクトは自動では2.xになりません。deno run -Ar jsr:@fresh/updateで$fresh/server.tsのimportやミドルウェアの書式はある程度置き換わります。構成ファイルは手で直します(Migration Guide)。
| 観点 | Fresh 1.x | Fresh 2.x |
|---|---|---|
| import元 | $fresh/server.ts | fresh(jsr:@fresh/core) |
| 開発・ビルド | dev.ts | vite.config.ts+client.ts |
| 設定・マニフェスト | fresh.config.ts・fresh.gen.ts | 削除 |
| ハンドラー・ミドルウェア | (req, ctx) | (ctx) |
| エラーページ | _404.tsx・_500.tsx | _error.tsx に統合 |
| 本番起動 | deno run -A main.ts | deno serve -A _fresh/server.js |
ハマりやすいのはデプロイ側です。Vite構成へ移行した場合は、ビルド時に本番用の入口が生成されるため、起動対象をmain.tsから_fresh/server.jsへ変更します。実際のファイル削除やタスクの書き換えの順序はDenoとFreshの開発環境セットアップの移行手順に沿って進めてください。
デプロイ先の選び方:Deno Deploy終了とCloudflare Workers・Node.js
Denoは2026年10月9日、Deno is joining Cloudflareで、Denoチーム全体がCloudflareに加わると発表しました。決まったことは次の4点です。
- Denoランタイムは、今後1年間はバグ修正とセキュリティ更新を月次でリリースし、その後は開発を終える(オープンソースとしては残る)
- Deno Deployは6か月運用したのち終了し、有料顧客にはCloudflare Workersへの移行支援を提供する
- JSRは運用を続け、基盤をCloudflareへ移す
- 発表本文にFreshへの言及はない
Freshのドキュメントは10月28日時点でも、Deno Deployを推奨のデプロイ先として案内しています(Deploy to Deno Deploy)。ほかの選択肢はdeno compile、Docker、Cloudflare Workersです。Workersへは@cloudflare/vite-pluginをvite.config.tsに足し、_fresh/server.jsのfetchを書き出す入口ファイルを置けば載せられます。ルートのハンドラーでDeno.openKv()などDeno固有のAPIを呼んでいる箇所はWorkersで動かないので、置き換えが要ります。設定ファイルの全文とKV・cronの移し方はDeno Deploy終了と移行手順:Cloudflare WorkersへのコードとKV・cronの移し方にまとめました。Deno自体をやめてBunやNode.jsへ移す場合の比較はDeno Bun 比較:開発終了が決まったDenoからBun・Node.jsへ移す判断とコードで扱っています。
Fresh 3は、Deno依存を外す方向で作り直されています。開発ブランチはVite 8とNitro 3の上に書き直されており、npmの@frsh/freshでアルファを配布しています。10月17日にはLuca Casonato氏が「Fresh 3はNode、Deno、そのほかNitroがビルド対象にできるランタイムで動く」と答えました。正式版の時期は示されていません。Nitroのプリセットの考え方はNitroとは?Nuxtを支えるサーバーエンジンの仕組みとv3移行の判断軸が参考になります。
Freshを採用する条件と見送る場面
2026年10月時点では、既存アプリの移行と長期運用する新規案件で採用方針を分けます。
- 既存のFresh 1.xアプリ:2.3へ上げ、デプロイ先をCloudflare WorkersかDockerへ移します。公式のCloudflare Workers向け手順はVite構成(2.x)が前提なので、Deno Deployの終了に合わせて移すなら2.3が出発点です。
- 既存のFresh 2アプリ:Deno KVなどDeno固有APIの使用箇所を洗い出し、Deno Deployの停止前に移します。Fresh 3は追わず、正式版が出てから評価します。
- 1年以上運用する新規案件:Fresh 2では始めません。Denoランタイムの開発は発表から1年で終わり、Fresh 3では作り直しが入るためです。2026年6月2日時点のFresh 3の比較資料(fresh2-comparison.md)では、
new App()のビルダー、static/フォルダ、defineヘルパー、cors・csrfなどの組み込みプラグインがFresh 3に無いと整理されています。 - 短期の社内ツールや検証:Fresh 2で問題ありません。島なしページのJSが0件になる性質は、管理画面のような静的寄りのUIで効きます。
Next.js・Astro・HonoXとの使い分け
同じ「サーバー描画中心」のフレームワークとの違いは次の表のとおりです。
| 観点 | Fresh 2 | Astro | HonoX | Next.js |
|---|---|---|---|---|
| UIライブラリ | Preact固定 | React・Vue等を混在可 | hono/jsx・React等 | React |
| 島の扱い | islands/に置く | client:ディレクティブ | islands/に置く | Client Component境界(島とは別方式) |
| 主な実行環境 | Deno | Node.js・各種アダプター | Workers・Node.js等 | Node.js・Vercel |
| 向く案件 | Deno前提の小中規模 | コンテンツ主体のサイト | Workers上のAPI+画面 | 大規模なReactアプリ |
コンテンツが多いサイトならAstroが素直です(AstroとReactの違いと使い分け)。Workersに載せる前提で同じ島の考え方を取りたいならHonoXが近い選択肢ですが、HonoXのREADMEがアルファ段階と明記しており、同じメジャー版の中でも破壊的変更が入ります(HonoXとは?Hono×Viteのメタフレームワークの使い方とIslands・デプロイ・採用判断【v0.1.62】)。Reactのエコシステムを丸ごと使う大規模アプリはNext.jsに分があります。
よくある質問
Fresh 2.0は正式リリースされていますか?
はい。2.0.0は2025年9月9日に正式版として公開されました。その1週間前の9月2日に出たのはベータです。最新の安定版は2026年4月28日の2.3.3です。
Denoの開発終了でFreshは使えなくなりますか?
すぐには使えなくなりません。Denoランタイムは発表から1年間、月次の保守リリースが続き、その後もオープンソースとして残ります。ただしDeno Deployは6か月後に終了するので、デプロイ先は移す必要があります。FreshはDeno社から独立しており、Fresh 3はNode.jsでも動く設計です。
Freshはビルドが必要ですか?
Fresh 2では本番前のビルドが必要です。標準のVite構成では、開発はdeno task dev(Vite)で動き、本番前にdeno task buildで_fresh/を作り、deno serve -A _fresh/server.jsで起動します。レガシーのBuilder構成でも、dev.tsのbuilder.build()で本番用のアセットを作ります。既定でビルド不要だったのは1.xです。
Freshで使うPreactとReactの違いは何ですか?
JSXやフックの書き方はほぼ同じで、Preactは本体が3kB(公式表記)と小さい点が違います。React向けライブラリはpreact/compat経由で多くが動きますが、Reactの内部実装に依存するものは動作確認が要ります。
Fresh 3はいつリリースされますか?
正式版の時期は公表されていません。2026年10月8日にnpmの@frsh/freshで3.0.0-alpha.202610031718が公開されています。Vite 8とNitro 3で作り直したアルファで、Fresh 2のAPIとは互換がない部分があります。