React

Wakuとは?Reactミニマルフレームワークの導入とNext.jsとの違い【2026年8月版】

Waku(ワク)は、React Server Components を前提に設計された小さなReactフレームワークです。npmで配布されている最新版は 1.0.0-beta.8 で、2026年7月24日にタグが打たれています。安定版1.0はまだ出ていません。この記事では、導入コマンドから src/pages のルーティング、静的生成と動的レンダリングの切り替え、デプロイ先ごとのアダプタ指定までを、公式リポジトリのコード例に沿って整理します。0.2x系の解説記事とは前提が変わっている箇所が複数あるため、移行時の破壊的変更もあわせて扱います。

まとめ

  • WakuはReact 19のサーバーコンポーネントとサーバーアクションを、フレームワーク独自APIをほとんど増やさずに使うための土台です。
  • npmパッケージ名は waku で、「Waku.js」「waku js」も同じものを指します。分散型メッセージングの Waku v2 とは無関係の別プロジェクトです。
  • 2026年8月2日時点のnpm最新は 1.0.0-beta.8。npm install waku で入るのはベータ版です。
  • 動作条件はNode.js 22.15.0以上(22系・24系・26系)、Reactは 19.2系にピン留めされています。
  • ルーティングは src/pages のファイル配置で決まり、各ページが getConfig で静的か動的かを宣言します。既定は静的です。
  • デプロイ先はVercel・Netlify・Cloudflareならコマンドだけで済みます。Deno・Bun・AWS Lambda(いずれも実験的)は src/waku.server.tsx を1ファイル足してアダプタを指定します。
  • 0.25.0でページ既定が静的になり、0.27.0でアダプタ方式が入れ替わりました。古い記事のコードをそのまま貼っても動きません。

結論を先に言えば、静的生成が中心のサイトとRSCの検証用途では噛み合い、長期保守が前提の業務システムでは噛み合いません。その根拠を導入手順から順に見ていきます。

ミニマルReactフレームワークWakuの設計方針と1.0.0-beta.8の現在地

フレームワーク独自APIを増やさない設計方針

Wakuの公式説明は「⛩️ The minimal React framework」です。公式ドキュメントはレンダリングの原則を「static at build time, fresh at request time, cache explicitly」と表現しています。ビルド時に静的化するか、リクエスト時に実行するかをページごとに宣言させ、その中間にある暗黙のキャッシュ層を持たない、という設計です。

コンポーネントは既定でサーバー実行です。状態やイベントハンドラが必要な箇所にだけ 'use client' を付けます。データ取得はサーバーコンポーネント内で await するだけで、専用のデータ取得APIはありません。サーバーアクション、useState、エフェクトはReact標準のものをそのまま使います。この考え方の前提となるサーバー/クライアントの境界については、React Server Componentsのサーバー・クライアントの使い分けを先に押さえておくと読み進めやすくなります。

バージョンの現在地と開発体制(1.0.0-beta.8・wakujsへの移管)

npmレジストリの latest タグは 1.0.0-beta.8 を指しています(2026年8月2日時点)。つまり npm install waku で入るのはベータ版です。GitHubのタグ一覧も v1.0.0-beta.8 が先頭で、該当コミットの日付は2026年7月24日です。

リポジトリの所在も変わりました。個人アカウントの dai-shi/waku から組織アカウントの wakujs/waku へ移管されています。ここで注意が必要なのは、旧URLがリダイレクトされない点です。移管後の2026年7月22日に同じパスへ別のフォークが作られており、github.com/dai-shi/waku を開くとスター0のフォークに着地します。issueやPRを追うときは wakujs/waku を直接開いてください。本体は2026年8月2日時点でスター6,382、オープンなissueが6件(別途プルリクエスト2件)、ライセンスはMITです。npmの週次ダウンロードは24,452件(2026年7月25日〜31日)でした。

項目 値(2026-08-02時点)
npm latest 1.0.0-beta.8
タグ日付 2026-07-24
Node ^22.15.0 / ^24 / ^26
React peer ~19.2.4
Vite ^8.0.0
ライセンス MIT
週次DL 24,452

数字の読み方としては、週2万件台というダウンロード規模は、実験的な個人プロジェクトの水準は超えているものの、Next.jsのような業務標準とは桁が違います。社内で採用する場合、詰まったときに日本語の実例を検索して解決する、という進め方は期待しにくい規模です。

npm create waku@latestでの導入手順とNode 22.15以上・React 19.2系の動作条件

Node 22.15以上・React 19.2系というバージョン条件

1.0.0-beta.8 の engines.node^26.0.0 || ^24.0.0 || ^22.15.0 です。Node 20系はすでに対象外で、22系でも 22.15.0 未満は弾かれます。Node 18のサポートは0.22.0(2025年4月2日)で終了しました。

peerDependencies はより厳しく、reactreact-domreact-server-dom-webpack がいずれも ~19.2.4 です。チルダ指定なのでパッチ更新しか許容されず、実質19.2系への固定です。Reactの最新は19.2.8なので現時点では範囲内に収まりますが、React 19.3が出た時点でWaku側の更新を待つことになります。Reactのバージョンを自分たちの都合で先に上げられない、という制約はプロジェクト計画に影響します。

ビルド基盤はVite 8です。RSCの変換は0.24.0(2025年8月5日)以降 @vitejs/plugin-rsc に委譲され、依存には @vitejs/plugin-react の v6系も含まれます。Vite 8世代でのReactプラグインの構成は、@vitejs/plugin-react v6の導入設定とBabel廃止の影響で扱っている内容がそのまま前提になります。サーバーはHono 4系です。

生成されるディレクトリ構成と3つの実行コマンド

プロジェクトの作成はスキャフォールドコマンド1本です。

npm create waku@latest
cd waku-project
npm install
npm run dev

開発サーバーは http://localhost:3000 です。npm run build 後の npm run start は既定で 8080 番を使い、埋まっていれば空いている番号へ繰り上がります。生成される構成は次のとおりです。

waku-project/
├── public/            静的アセット
├── src/
│   ├── components/    再利用コンポーネント
│   ├── middleware/    サーバーミドルウェア(任意)
│   ├── pages/         ファイルルーティング
│   │   ├── _layout.tsx
│   │   ├── about.tsx
│   │   └── index.tsx
│   ├── global.d.ts
│   └── styles.css
├── package.json
├── tsconfig.json
└── waku.config.ts

開発サーバーの起動中は、ページを追加するたびに src/pages.gen.ts が生成され、型付きのルート定義が更新されます。このファイルは手で編集しません。

src/pagesのファイルルーティングとstatic/dynamic切り替えの実装

index・[slug]・[…catchAll]とレイアウトの対応関係

ルートは src/pages 配下のファイル配置で決まります。各ファイルはReactコンポーネントをデフォルトエクスポートし、必要に応じて getConfig を添える形です。既定が静的なので、静的のままでよければ getConfig は省略できます。

// ./src/pages/index.tsx
export default async function HomePage() {
  return (
    <>
      <title>Waku</title>
      <h1>Home</h1>
    </>
  );
}

export const getConfig = async () => {
  return { render: 'static' } as const;
};

動的セグメントは角括弧で表します。[slug].tsx と書くと、セグメント名と同じ名前のpropsがコンポーネントに渡ります。不定個のセグメントを受けるワイルドカードは [...catchAll].tsx です。レイアウトは _layout.tsx で、children を受け取って入れ子に適用されます。

getConfigのrender指定とstaticPathsが必須になる条件

レンダリング方式はページ単位の宣言です。render: 'static' はビルド時のプリレンダリング、render: 'dynamic' はリクエスト時のサーバー実行を意味します。ページとレイアウトの既定は静的、APIハンドラの既定は動的です。

ここに落とし穴があります。動的セグメントを静的にプリレンダリングする場合、ビルド時にどのパスを生成するか分からないため、staticPaths の指定が必須です。

// ./src/pages/blog/[slug].tsx
import type { PageProps } from 'waku/router';

export default async function BlogArticlePage({
  slug,
}: PageProps<'/blog/[slug]'>) {
  const data = await getData(slug);
  return <article>{data.title}</article>;
}

export const getConfig = async () => {
  return {
    render: 'static',
    staticPaths: ['introducing-waku', 'introducing-pages-router'],
  } as const;
};

staticPaths は非同期に組み立てても構いません。ネストしたセグメントを静的化する場合は [['same-category', 'some-product'], ['same-category', 'another-product']] のように、セグメントの順序どおりの配列を渡します。ユーザーごとに内容が変わるダッシュボードのような画面は render: 'dynamic' にします。

Linkとルーター操作によるクライアント遷移

内部リンクには waku からインポートする Link を使い、遷移先を to プロパティで渡します。クエリ付きの遷移は <Link to={{ to: '/search', search: { q: 'waku' } }}> の形です。クライアントコンポーネントからは useRouter で現在の pathqueryhash を読み、pushreplaceprefetchreloadbackforward を呼び出せます。リクエストを横断する処理はルーターの担当ではありません。認証チェックやヘッダー操作は src/middleware に置くHonoのミドルウェアが受け持ちます。

サーバーコンポーネント・Server Actions・API Routesの書き分け

‘use client’を付ける境界の判断

既定がサーバー実行なので、指示子を書く必要があるのはクライアント側だけです。データベースへの問い合わせはサーバーコンポーネント内で直接書けます。

import db from 'some-db';
import { Gallery } from '../components/gallery';

export const Store = async () => {
  const products = await db.query('SELECT * FROM products');
  return <Gallery products={products} />;
};

一方、useState やクリックハンドラを使うコンポーネントには先頭に 'use client' を書きます。境界を細かく切るほどクライアントに送られるJavaScriptは小さくなります。

‘use server’によるミューテーションとフォーム連携

更新系の処理はサーバーアクションとして切り出します。ファイル先頭に 'use server' を置き、クライアントコンポーネントからは通常の関数としてインポートします。

// ./src/actions/send-message.ts
'use server';

import db from 'some-db';

export async function sendMessage(formData: FormData) {
  const message = formData.get('message');
  await db.messages.create(message);
}
// ./src/components/contact-form.tsx
'use client';

import { sendMessage } from '../actions/send-message';

export const ContactForm = () => {
  return (
    <form action={sendMessage}>
      <textarea name="message" rows={4} />
      <button type="submit">Send</button>
    </form>
  );
};

ここで引数の型を間違えやすいので注意してください。<form action={...}> に渡した関数は、Reactが FormData 1個を引数にして呼びます。sendMessage(message: string) のように書くと、文字列のつもりの引数に FormData が入ります。ボタンのクリックハンドラから任意の引数で呼ぶ場合と、フォーム送信から呼ぶ場合とで、シグネチャを分けてください。

src/pages/_apiに置くエンドポイントとパス変換の注意

HTTPエンドポイントは src/pages/_api ディレクトリに置き、HTTPメソッド名の関数をエクスポートします。パスパラメータは ApiContext で型付けされます。

// ./src/pages/_api/users/[id].ts
import type { ApiContext } from 'waku/router';

export async function GET(
  _req: Request,
  { params }: ApiContext<'/users/[id]'>,
) {
  const { id } = params;
  return Response.json({ id, message: `Hello user ${id}` });
}

ここで見落としやすいのが公開パスの読み替えです。URLからは _api が取り除かれるため、src/pages/_api/contact.ts の応答先は /contact になります。フェッチ先を /_api/contact と書くと404です。なお、RSSやサイトマップのように内容が固定のレスポンスは、APIルートでも getConfigrender: 'static' を指定して静的化できます。

React 19のタグhoistingで完結するメタデータとアダプタ方式のデプロイ

title・meta・linkを本文に書くだけで済むメタデータ指定

Wakuはメタデータ用の専用APIを持ちません。titlemetalink をページやレイアウトのJSXにそのまま書くと、ドキュメントのheadへ引き上げられます。React 19のタグホイスティングをそのまま利用する形です。共通のファビコンやOGP画像は _layout.tsx に、ページ固有のタイトルと説明文は各ページに書きます。記事タイトルのように動的に決まる値は、非同期コンポーネントで取得してから <title>{metadata.title}</title> のように出力します。

主要ホスティングのデプロイ手順と、サーバーエントリを書く場合の境界

Node.js環境であれば waku start、もしくはビルド成果物の dist/serve-node.js を直接実行します。全ページ静的の構成なら dist/public をそのまま配信できます。

主要なホスティングは、コード追加なしでコマンドだけで済みます。Vercelは vercel、Netlifyは NETLIFY=1 npm run build の後に netlify deploy、Cloudflare Workersは CLOUDFLARE=1 npm run build の後に wrangler deploy です。この段階では src/waku.server.tsx を書く必要はありません。

サーバーエントリを自分で書くのは、実行ランタイムを明示したい場合と、関数を一切デプロイせず完全な静的サイトとして出したい場合です。後者の書き方が次のコードで、static: true がその指定にあたります。

// ./src/waku.server.tsx(完全静的で出す場合)
import { fsRouter } from 'waku';
import adapter from 'waku/adapters/cloudflare';

export default adapter(
  fsRouter(import.meta.glob('./pages/**/*.{tsx,ts}')),
  { static: true },
);

このオプションはサーバー関数を出力しない指定です。公式READMEは、Pure SSGでは動的レンダリング・サーバーアクション・APIルートが動作しないと明記しています。動的ページを持つアプリに static: true を付けると、それらは配信対象から外れ、該当URLは404を返します。関数を使う通常構成では付けないでください。

インポート元は、例に挙げた waku/adapters/cloudflare のほか waku/adapters/vercelwaku/adapters/netlifywaku/adapters/denowaku/adapters/bunwaku/adapters/aws-lambda から選びます。第2引数はアダプタごとに異なり、DenoとBunは引数を取らず、AWS Lambdaは { streaming: false } を任意で受け取ります。この3つは公式READMEで実験的(experimental)と明記されている点に注意してください。特定のホスティングに寄せない最小構成が必要なら、waku.config.tsunstable_adapter: 'waku/adapters/edge' を指定します。オプション名の unstable_ が示すとおり、この指定は今後変わり得ます。

0.2x系から1.0系までに入った破壊的変更と、Wakuを採用しない判断基準

移行時に書き換えが要る変更点と、追跡先がGitHub Releasesへ移った経緯

Wakuを扱った日本語記事の多くは0.19〜0.21世代を前提にしています。その後の変更を追うときに引っかかるのが、記録場所が途中で変わったことです。リポジトリの CHANGELOG.md は冒頭で「v1.0.0-alpha.0 以降の変更はGitHub Releasesを見よ」と宣言しており、0.27.5(2025年12月17日)で更新が止まっています。1.0系の破壊的変更はCHANGELOGを読んでも出てきません。

両方をたどると、コードの書き換えを伴う主な変更は次のとおりです。

日付 変更点 記録場所
0.22.0 2025-04-02 Node 18終了・APIルート追加 CHANGELOG
0.24.0 2025-08-05 RSCをViteプラグインへ委譲 CHANGELOG
0.25.0 2025-08-12 ページ既定を静的へ(Breaking明記) CHANGELOG
0.26.0 2025-08-25 旧機能の削除 CHANGELOG
0.27.0 2025-10-27 アダプタ方式の刷新(Breaking明記) CHANGELOG
1.0.0-alpha.0 2025-12-23 エントリファイルを waku.server/waku.client へ改名 Releases
1.0.0-alpha.1 2026-01-06 api/ を _api/ へ改名しURLから接頭辞を除去 Releases
1.0.0-beta.0 2026-05-12 fsRouterのbaseオプション削除・Node 20終了 Releases

影響が大きいのは0.25.0です。既定が静的に変わったため、リクエストごとに変わる内容を返すページで render: 'dynamic' を明示していないと、ビルド時の状態が固定されて配信されます。ビルドは通り、エラーも出ません。表示だけが古いままになります。

0.2x系のAPIルートを持ち込む場合は、1.0.0-alpha.1の改名に注意してください。src/pages/api/src/pages/_api/ になり、同時に公開URLから接頭辞が外れました。/api/contact というURLを維持したいなら、ファイルを src/pages/_api/api/contact.ts へ置く必要があります。

もう一点、公式の位置づけも押さえておくと判断しやすくなります。2025年12月23日の1.0.0-alpha.0で「公開APIは安定した」と宣言されました。ただし宣言の後も、APIルートのディレクトリ改名(1.0.0-alpha.1)、fsRouter のオプション削除とNode 20終了(1.0.0-beta.0)といった破壊的変更が続いています。安定宣言は「大きな再設計はしない」という意味であって、無改修で追随できる保証ではありません。

ベータ運用と依存固定が許容できないプロジェクトでの見送り基準

Wakuを採用しない方がよい条件は、はっきりしています。

第一に、依存パッケージの安定版のみを使う規定がある案件です。npmの latest がプレリリースを指している以上、npm install waku はベータの導入になります。バージョン固定ポリシーとの整合を毎回説明する手間が発生します。

第二に、Reactのバージョンを自分たちの判断で上げたい案件です。~19.2.4 という指定は19.2系への固定であり、React側の新しいマイナー版に追随できるかはWakuの更新次第になります。他の依存ライブラリがReactの新しいマイナー版を要求し始めた時点で、身動きが取れなくなります。

第三に、保守期間が数年単位で、担当者の交代が見込まれる業務システムです。2025年4月から10月までの半年で、レンダリング既定とデプロイ定義という土台部分が入れ替わりました。この頻度は、追随を担当する人が常にいる前提でなければ現実的ではありません。エンタープライズ用途での本命は依然としてNext.js 16の主要機能の側にあり、フレームワーク選定の全体像はNext.jsとNuxtの違いと選び方の観点で整理すると判断しやすくなります。

逆に噛み合うのは、コーポレートサイトやキャンペーンページのように、静的生成が中心で公開後の改修が短いサイクルで回る案件です。RSCの構造をそのまま学べるため、React 19の設計を検証する社内の試作にも向きます。この用途であれば、ベータであることの実害はほとんどありません。

よくある質問

Waku.jsとWakuは別のものですか

同じものです。npmで配布されているパッケージ名は waku で、公式サイトは waku.gg で公開されています。JavaScript製のフレームワークであることを示すために「Waku.js」「waku js」と表記されることがありますが、公式の名称は Waku です。読み方は公式ドキュメントが「Waku (wah-ku) or わく」と明記しています。なお、同じ名前でも分散型メッセージングプロトコルの Waku v2(npmの js-waku@waku/sdk)は完全に別のプロジェクトです。この記事で扱うのはReactフレームワークの方で、パッケージ名は waku の1語です。

npmで入るWakuの最新バージョンはどれですか

2026年8月2日時点で、npmの latest タグは 1.0.0-beta.8 を指しています。該当タグのコミット日は2026年7月24日です。安定版の1.0.0はまだ公開されていません。npm install waku と実行するとベータ版が入るため、安定版のみを使う方針の環境では、事前にバージョン指定の運用を決めておく必要があります。

WakuとNext.jsはどちらを選ぶべきですか

更新頻度と規模で分けます。Wakuが向くのは、静的生成が中心で改修サイクルの短いサイトと、RSCの挙動を検証する試作です。Next.jsが適するのは、長期保守が前提の業務システムや、画像最適化・国際化を標準装備として求める案件です。Wakuの公式ドキュメントにはこれらに対応する章がなく、必要なら自前で用意することになります。

既存のReactプロジェクトにWakuを後から導入できますか

部分的な導入はできません。Wakuはビルド基盤とルーティングを含む土台なので、既存のViteプロジェクトへライブラリとして足す形にはならず、src/pages の構造へ画面を移す作業が必要です。前提として、Reactを19.2系に揃え、Node.jsを22.15.0以上にする必要があります。サーバーコンポーネントを使う以上、静的ホスティングだけで完結していた構成にはサーバー実行環境も加わります。移行を検討するなら、まず新規の小さなページ群で試すのが現実的です。

ルーティングをファイル配置ではなくコードで定義できますか

できます。ファイル配置による解決は fsRouter が担っていますが、ルートをコードで組み立てる createPages も用意されています。インポート元は import { createPages } from 'waku'; です。さらに低レベルなAPIは import { unstable_defineRouter } from 'waku/router/server'; で取得します。名前のとおり将来変わり得る扱いです。混同しやすいのですが、waku/routerPagePropsApiContext といった型だけを公開しており、ルーターを構築する関数は入っていません。npm create waku@latest が生成する初期構成は src/pages のファイルルーティングで、公式ドキュメントの説明もそちらが中心です。特段の理由がなければファイル配置に寄せる方が情報を探しやすくなります。

関連記事

資料請求

RELATED POSTS 関連記事