第2回

最小Reactフレームワーク Waku をCloudflareで動かす

Jotai作者による最小Reactフレームワーク Waku を実際に試す。プロジェクト作成からServer Components、Cloudflareへのデプロイまでを手順を追って動かしながら解説する。

·14分で読める

制作・運営:React Guide 運営者

教材の範囲:以下は別プロジェクトで試す手順とコード例です。記事内の見本は静的な説明用表示です。導入先のフレームワーク・外部サービスまでの実行確認は含めていません。

たける
たける Server Components を試したいんですけど、Next.js はいきなり大きくて設定も多くて。もっと小さく「RSCだけ」を触れる方法ないですか?できればこのサイトみたいに Cloudflare に置きたいです。
りこ
りこ それなら **Waku** がいい。Jotai や Zustand を作った Daishi Kato さんの「最小Reactフレームワーク」。Vite と Hono の上に乗っていて、React 19 の Server Components / Server Functions をそのまま使える。Cloudflare 用のアダプタもある。

「React最新ラボ」第2回。今回はフレームワークを丸ごと立ち上げて動かす。Server Components(RSC)を一番小さい構成で試し、最後に Cloudflare へデプロイする。

導入時は生成されたpackage.jsonとロックファイルで使用版を記録してください。現行の公式ドキュメントのNode.js要件は22.15以上の22系、24系、26系です。

そもそも RSC(Server Components)で何が嬉しい?

ユナ
ユナ ざっくり言うと「コンポーネントをサーバ側でレンダリングして、クライアントに送るJavaScriptを減らす」仕組み。コンポーネントの中で直接DBやファイルにアクセスできる。

これまでの作り方とRSCの違いを並べると分かりやすい。

これまで(CSR) RSC
データ取得 useEffect + fetch をクライアントで実行 コンポーネント自体が async。サーバで取得
クライアントに届くもの コンポーネントのJS+取得後のデータ RSCの出力データ。初回のHTML生成はSSRと組み合わせる
秘密情報 ブラウザへ渡さずサーバーで扱う サーバーの環境変数で管理し、props・返り値へ含めない
サーバでレンダ
→
RSC出力+初回HTML
→
必要な所だけJS

プロジェクトを作る

Cloudflare に置く前提なら、Cloudflare のCLI(C3)から作るのが速い。

npm create cloudflare@latest my-waku-app -- --framework=waku

対話では次のように選ぶ。

  • テンプレート:Framework Starter → Waku
  • git で管理:Yes
  • すぐデプロイ:No(まずローカルで動かす)

Cloudflare 抜きで純粋に Waku だけ試すなら npm create waku@latest でもいい。

作ったら開発サーバを起動する。

cd my-waku-app
npm run dev
# → http://localhost:3000

ファイル構成:src/pages がそのままルートになる

Waku は src/pages ディレクトリのファイルベースルーティング。ファイルを置けば、そのままURLになる。

src/
├── pages/
│   ├── _layout.tsx     # 全ページ共通のレイアウト
│   ├── index.tsx       # /
│   └── about.tsx       # /about
├── components/
└── waku.server.ts      # サーバ/ルーティング設定(生成構成によって異なる)

各ページは「default で React コンポーネントを export」し、必要に応じて「getConfig という名前付き関数を export」してレンダリング方法を指定する。

サーバコンポーネントを書く(デフォルトでサーバ実行)

src/pages 以下のコンポーネントは、何も書かなければサーバコンポーネント。async にできて、中で直接 fetch やDBアクセスを書ける。

// ./src/pages/index.tsx
export default async function HomePage() {
  // ここはサーバで動く。fetch も DB も直接書ける
  const res = await fetch('https://api.github.com/repos/wakujs/waku')
  if (!res.ok) throw new Error(`GitHub API: HTTP ${res.status}`)
  const repo = await res.json()

  return (
    <main>
      <h1>Waku を試す</h1>
      <p>GitHub ★ {repo.stargazers_count}</p>
    </main>
  )
}

// レンダリング方法を指定(dynamic = リクエストごとにサーバでレンダ)
export const getConfig = async () => {
  return { render: 'dynamic' } as const
}

render: 'static' にすれば、ビルド時にHTMLを生成する(静的化/SSG)。ブログのように内容が変わらないページに向く。

ユナ
ユナ `useEffect` も、ローディング状態の管理もない。「サーバで取る → 描画済みで届く」だけ。データ取得の定番パターンがまるごと消えるのが体感ポイント。
表示イメージ(操作・計測結果ではありません):/ にアクセスしたときの表示(サーバで取得済みのHTMLが届く)

Waku を試す

GitHub ★ 13200

インタラクティブな所だけ 'use client'

クリックや useState が必要な部分は、クライアントコンポーネントに切り出す。ファイルの先頭に 'use client' を書くだけ。

// ./src/components/counter.tsx
'use client'

import { useState } from 'react'

export const Counter = () => {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount((c) => c + 1)}>カウント: {count}</button>
}

これをサーバコンポーネントから普通に import して置けばいい。「ページ全体はサーバ、ボタンだけクライアント」という構成になる。

サーバ処理を呼ぶ:'use server'(Server Functions)

フォーム送信やDB書き込みなど「サーバでだけ動かしたい処理」は Server Function にする。専用ファイルの先頭に 'use server' を置く。

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

export async function sendMessage(message: string) {
  // ここはサーバでだけ実行される。DB接続などをここに書く
  console.log('received:', message)
}
// ./src/components/contact-button.tsx
'use client'

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

export const ContactButton = () => {
  return <button onClick={() => sendMessage('Hello!')}>送信</button>
}

'use server' のファイルから export した関数は、そのままサーバのエンドポイントになる。クライアント側からは「ただの関数呼び出し」に見えるのに、中身はサーバで動く。

Cloudflareに公開する前の確認

CloudflareのC3で作ったプロジェクトでは、生成されたpackage.jsonのscriptsとCloudflare設定を使う。異なるWakuの版向けのアダプタを上書きしない。

npm run build
npm run deploy

deployはCloudflareのアカウントへ公開する操作。先にローカルでページ・カウンター・HTTPエラー時の表示を確認し、生成先とアカウントを確認して実行する。

Cloudflare公式のWakuガイドの現行手順を基準にする。当記事の見本は公開成功の証拠ではなく、今回の見直しではこのフレームワークの外部デプロイまでは行っていない。

Server Functionsは外部から呼べる入口になる。TypeScriptの引数型だけでは受信データの検証にも認証にもならない。保存処理を追加するときは、サーバー側で入力と操作権限を検証する。

つまずきやすい所

  • サーバとクライアントの境界:window や localStorage などブラウザ専用APIはサーバコンポーネントで使えない。使う部分は 'use client' 側へ移す。
  • 使用版の確認:バージョンによってAPIや設定の細部が変わることがある。詰まったら必ず公式ドキュメントで現行の書き方を確認する。

まとめ

  • Waku は Vite + Hono ベースの最小RSCフレームワーク。src/pages のファイルベースルーティング
  • 何も書かなければサーバコンポーネント。async でデータ取得をそのまま書ける
  • インタラクティブは 'use client'、サーバ処理は 'use server' で切り分ける
  • Cloudflare向けの生成設定を確認し、ローカルビルド後に公開する

次回は第3回。見た目の話に戻って、アニメ付きUIコンポーネント集 React Bits を、このサイトと同じ Vite + Tailwind 環境に最短で入れて動かす。

確認してみる

サーバー取得を失敗させたときの表示と、Counterがクリックで増えることを別々に確認する。ブラウザへ送られるpropsに秘密情報を含めない。

仕様・手順の参照先

#react#waku#rsc#cloudflare#intermediate