はじめに
React Server Componentsを使いたいけれど、Next.jsは機能が多すぎて把握しきれない。かといって自前でRSC対応のフレームワークを組むのはさすがに骨が折れる——そんな悩みを抱えたことはないでしょうか。
さらに「デプロイ先はCloudflareに寄せたい」という要件が加わると、選択肢は一気に狭まります。そこに登場したのが、Cloudflare Workers上で動くことを前提に設計されたReactフレームワーク「RedwoodSDK」です。今回はこのRedwoodSDKについて、特徴から実際の使い方までを見ていきます。
RedwoodSDKとは
RedwoodSDKは「A simple framework for humans」を掲げる、サーバーファーストのReactフレームワークです。Vite上に構築されており、Cloudflare Workers・D1(データベース)・R2(ストレージ)といったCloudflareのプラットフォーム機能とネイティブに統合されています。
「魔法を使わない」「合成可能である」「Web標準に従う」「サーバーファーストである」という4つの原則を掲げているのが特徴で、独自のコード生成や暗黙のルーティングに頼らず、素のJavaScript関数とWeb標準のRequest/Responseを積み重ねてアプリケーションを組み立てていくスタイルを取っています。
主な特徴
- Vite-firstな構成 - 設定ファイルは最小限で、Viteのエコシステムをそのまま活かせます
- React Server Components対応 - ルートからJSXを直接返すだけでサーバーレンダリングされます
- 型安全なルーター - ミドルウェアや「interrupter」と呼ばれる割り込み処理をルート単位で組み込めます
- Cloudflareとの統合 - D1やR2、Queuesなどのバインディングをそのままコードから扱えます
useSyncedStateによるリアルタイム同期 - WebSocketを自前で書かなくても複数クライアント間で状態を共有できます
インストール
新規プロジェクトはcreate-rwsdkコマンドで作成します。
npx create-rwsdk my-app
cd my-app
pnpm install
pnpm dev
開発サーバーが起動したら、あとはブラウザでlocalhostにアクセスするだけで動作確認ができます。本番環境へのデプロイは以下のコマンドでCloudflare Workersに対して行います。
pnpm release
基本的な使い方
RedwoodSDKでは、src/worker.tsxが起点となり、defineAppにルートの配列を渡してアプリケーションを構築します。
// src/worker.tsx
import { defineApp } from "rwsdk/worker";
import { route, render } from "rwsdk/router";
import { Document } from "./Document";
import { Home } from "./pages/Home";
export default defineApp([
render(Document, [
route("/", Home),
route("/ping", function () {
return new Response("pong");
}),
]),
]);
ルートハンドラーはWeb標準のResponseを返すこともできますし、JSXをそのまま返すこともできます。JSXを返した場合はサーバー側でレンダリングされ、HTMLとしてクライアントに届きます。
// src/pages/Home.tsx
export function Home() {
return (
<main>
<h1>Hello, RedwoodSDK!</h1>
<p>これはサーバーでレンダリングされたコンポーネントです。</p>
</main>
);
}
認証チェックのような割り込み処理を挟みたい場合は、ハンドラーの前に関数を並べます。
// src/worker.tsx(抜粋)
route("/admin", [
({ ctx }) => {
if (!ctx.user?.isAdmin) {
return new Response("Forbidden", { status: 403 });
}
},
AdminDashboard,
]);
この関数はリクエストごとに実行され、Responseを返した時点でその後の処理が中断されます。認証・認可・ロギングなどをルートごとに差し込みたいときに使える仕組みです。
実践的なユースケース
サーバー関数でフォームを処理する
クライアントから直接サーバー側の処理を呼び出したい場合は、"use server"ディレクティブを使ったサーバー関数が便利です。データベースアクセスにはプレースホルダを使い、SQLインジェクションを避けます。
// src/actions/todos.ts
"use server";
import { db } from "../db";
export async function createTodo(formData: FormData) {
const title = formData.get("title");
await db.query("INSERT INTO todos (title) VALUES (?)", [title]);
}
// src/pages/TodoForm.tsx
import { createTodo } from "../actions/todos";
export function TodoForm() {
return (
<form action={createTodo}>
<input type="text" name="title" placeholder="やることを入力" />
<button type="submit">追加</button>
</form>
);
}
フォームのactionにサーバー関数を渡すだけで、クライアント側からfetchを書く必要はありません。
リアルタイムなカウンターを作る
useSyncedStateを使うと、WebSocketの実装を意識せずに複数クライアント間で状態を共有できます。
// src/components/LiveCounter.tsx
"use client";
import { useSyncedState } from "rwsdk/realtime";
export function LiveCounter() {
const [count, setCount] = useSyncedState("counter", 0);
return (
<button onClick={() => setCount(count + 1)}>
現在のカウント: {count}
</button>
);
}
同じキー(この例では"counter")を参照しているクライアント同士は自動的に状態が同期されるため、簡易的な共同編集機能やライブダッシュボードを短いコードで実現できます。
まとめ
RedwoodSDKは、React Server ComponentsとCloudflareのプラットフォーム機能を、余計な抽象化を挟まずに組み合わせられるフレームワークです。ルーティングはWeb標準のRequest/Responseに素直に従い、サーバー関数やリアルタイム同期といった機能もシンプルなAPIで提供されています。
「Next.jsほど大きなフレームワークは要らないが、RSCとCloudflareの恩恵は受けたい」という場合には、有力な選択肢になるはずです。まずはnpx create-rwsdkでプロジェクトを作り、/pingのような小さなルートから触ってみることをおすすめします。