Show, don't claim
このページが送る JavaScript は 12 KB。
このサイトで動く部分すべて、風のアニメーション・コピーボタン・スクロール演出を合わせて、Preact 込みで gzip 後 12 KB です。島の無いページは 0 KB。参考までに、React と ReactDOM だけで gzip 後およそ 45 KB。まだ自分のコードを 1 行も書いていない段階で、です。
このサイト自身の本番ビルドで実測(このサイトは Nowaki 製です)。同一のカウンタ1個アプリで直接比較すると、Nowaki ページの first-load は約 10 KB(gzip)。Astro と同等で、Next の app-router 基準(約 103 KB)のおよそ 1/5 です。benchmarks/head-to-head.mjs で再現できます。
打鍵ごとに感じる速さ。
多くの JavaScript フレームワークは、起動・変換・再ビルドを JavaScript ツールチェーンの上で行います。Nowaki はそのパイプライン全体を Rust(oxc)で動かすので、JavaScript バンドラーが温まりきる前に dev サーバーは応答しています。
サンプルアプリで実測した dev サーバー起動。
oxc による変更ファイルの再変換。JS バンドラーのウォームアップなし。
本番出力: モジュールを1スコープへ連結、ツリーシェイク、content-hash、ソースマップ付き。
何もない状態から、Rust なしで動くアプリへ。
npm create nowaki@latest雛形作成
file-based の routes/ と islands/ を生成。CLI はプリビルドのバイナリなので、Rust を入れる必要はありません。
npm run dev開発
Rust の oxc パイプラインがオンデマンドで変換。島はハイドレートし、それ以外は HTML のまま。エラーは全画面オーバーレイで表示。
npm run build · start公開
scope-hoisting 済み・content-hash 付きの ESM と SSR モジュールを本番配信。あるいは静的に prerender して CDN へ。
ルートを書き、島をマークし、フォームを処理する。
ルートは、サーバーでのみ走る任意の loader を持つコンポーネント。非 GET リクエストはルートの action が処理します。ブラウザでハイドレートするのは islands/ のものだけです。
loader 付きルート
// routes/blog/[slug].tsx
import Comments from "../../islands/Comments.tsx";
// runs on the server only
export const loader = async ({ params }) => ({
post: await db.post(params.slug),
});
export default function Post({ data }) {
return (
<article>
<h1>{data.post.title}</h1>
<Comments postId={data.post.id} /> // only this hydrates
</article>
);
}action 付きフォーム
// routes/guestbook.tsx
export const loader = (ctx) => ({ entries: read(ctx) });
// a non-GET request runs the action
export async function action(ctx) {
const form = await ctx.formData();
ctx.setCookie("guestbook", add(form.get("msg")));
return ctx.redirect("/guestbook"); // PRG
}ファイル規約
routes/_layout.tsx共有レイアウト、ディレクトリ単位でネストroutes/_middleware.tsルート前に実行。認証・リダイレクト・ヘッダroutes/blog/[slug].tsx動的ルート + server loaderroutes/api/posts.tsGET / POST ハンドラ、streaming Responseroutes/_404.tsx · _500.tsx未一致ページとエラーページislands/Counter.tsxブラウザでハイドレート。それ以外はしない。Next・Astro と並べて、正直に。
どのアイデアも単体では新しくありません。Nowaki の賭けはその組み合わせです。誇張なしで、実際にどこが違うかを示します。
| . | Nowaki | Next.js | Astro |
|---|---|---|---|
| サーバーリアクティブ島(クライアントJSゼロ)Only Nowaki | Jetstream — 状態はサーバー、HTML パッチを WebSocket で push、コンポーネントJSゼロ | RSC 再描画は React ランタイムを積む | Server Islands は SSR を遅延(一発・生更新ではない) |
| ツールチェーン | Rust (oxc) を自作 — 本番リクエストパスも Rust | Turbopack (SWC)、本番はJS | Vite → Rolldown、本番はJS |
| デフォルトの JavaScript | ゼロ、島だけ | React を送り hydrate | ゼロ、島だけ |
| フルスタックなアプリ DX | routing・loader・action・middleware・API・サーバー関数 | あり、成熟 | 成長中・コンテンツ寄り |
| ツールチェーンの言語なしで入る | npm、Rust 不要 | npm | npm |
| 成熟度 | alpha | 成熟・巨大なエコシステム | 成熟 |
ここだけ: 状態を持ち生更新するのに、コンポーネントJSをゼロで送る島。状態はサーバーが持ち、HTML パッチを WebSocket で押し出します — LiveView の発想を島に融合。RSC 再描画は React を積み、Astro の Server Islands は初期描画を遅延するだけで生更新はしません。これが両者にない一点です。
サーバー関数・ストリーミングSSR・プラグイン・デプロイアダプタは parity(同等)で差別化ではありません — Astro/Next にもあります。本当の強みは Jetstream、Rust の本番ランタイム、そして「フルスタック+JSゼロ+npmインストール」を同時に満たすこと。Next と Astro は成熟し実戦で揉まれていますが、Nowaki は alpha です。
どんなアプリに向くか。
Nowaki が向くのは、コンテンツ主体だが動的なアプリです。認証付きのマーケ、対話ウィジェット入りのドキュメント、本物のサーバーデータを持つ管理画面、EC。本文とサーバーデータが大半なのに、全 hydrate のフレームワークだと1ページに数百 KB の JavaScript を送ってしまう、その層です。
向いている
- Next.js 風にアプリを書くが、各ページの大半は静的コンテンツとサーバーデータ。
- フォーム・認証・動的ルートが欲しいが、全ページにクライアントランタイムの代金は払いたくない。
- Remix 風の loader / action モデルが好きで、それを使う所にだけ JS を送りたい。
まだ得意でない
- ほぼ全部が状態を持つ、全面インタラクティブな SPA。島はそこでは窮屈で、全 hydrate や RSC の方が合います。
- 今日の本番クリティカルな用途。Nowaki は alpha で、API はまだ動きます。
静的サイトジェネレータではない、本物のフレームワーク。
Next.js / Remix 系譜の動的なアプリ向けに作られ、実際にプロダクトを載せられる部品が揃っています。
フルスタックなルーティング
file-based の routes/ に、ネスト可能な _layout・_middleware・server loader・フォーム用の action、そしてメソッド分岐と streaming に対応した api/ ハンドラ。
デフォルトで島・JS ゼロ
ページはサーバーで HTML に描画。islands/ 配下のコンポーネントだけが配信・ハイドレートされ、ページが払うのは実際に使う分だけ。
サーバー関数("use server")
"use server" ディレクティブを持つモジュールは RPC 境界になります。export はサーバーにだけ残り、クライアントには fetch する極小プロキシだけ(実装もサーバー専用依存も出ません)。dispatch は allowlist 制で、getContext() からリクエストの cookie/ヘッダを読めます。
Jetstream island(サーバーリアクティブ)
島を export const live にするとコンポーネント JS をクライアントに送りません。状態はサーバーに置き、クリックは WebSocket で届き、Rust サーバーが再描画して HTML パッチを push、~2 KB のランタイムが morph で当てます。presence・ハートビート・接続スケールも内蔵。クライアント島(楽観UI)と同一ページで共存します。
Rust ツールチェーン (oxc)
パース・変換・解決・バンドル・minify・スコープホイスティングが Rust で動作。高速なコールドスタート、数ミリ秒の再ビルド、再起動をまたぐ永続ディスクキャッシュ。
npm で入る、Rust 不要
CLI は npm の optionalDependencies でプリビルドのネイティブバイナリとして配布。cargo もツールチェーンも postinstall も不要。
島間 SPA ルーター
島のあるページ間の遷移はクライアント側で即時、prefetch とスクロール復元つき。島の無いページは JS ゼロのまま通常遷移。
CSS Modules・アセット・ソースマップ
クラス名をスコープ化する *.module.css、画像やフォントのハッシュ付き import、dev/prod 両方の end-to-end ソースマップ。
npm エコシステムそのまま
SSR は Rust が管理する Preact の Node サイドカーで動くので、既存のパッケージがそのまま使えます。
正直な開発体験
全画面エラーオーバーレイ、コードフレーム診断、ホットリロード、保存時の島ホットスワップ。
alpha について正直に。
Nowaki はまだ若いですが、コアは実在し、ヘッドレス Chrome まで含めて端から端まで検証済みです。現在地を正確に示します。
今できること
- dev / build / start / prerender
- レイアウト・ミドルウェア・action・API ルート
- Islands + 島間 SPA ルーター
- サーバー関数("use server")RPC
- Jetstream island: サーバーリアクティブ・クライアント JS ゼロ
- Jetstream の presence・接続スケール(ハートビート・上限)
- プラグイン仮想モジュール(resolveId / load)+ transform フック
- CSS Modules・アセット import・ソースマップ
- スコープホイスティング済みの本番バンドル
- デプロイアダプタ: Node・静的・Bun・Deno・Cloudflare edge
- ストリーミング SSR・設定プラグイン・TSRX(.tsrx)島
- Next・Astro との直接ベンチ
- Rust 本番ホットパス + npm 経由の Rust 不要インストール
ロードマップ
- 状態保持(prefresh)HMR
- TSRX 島の scoped CSS
- RSC 風のストリーミング境界
- 公開 API の安定化(1.0 へ)