ガイド
Server Actions
Node 上で実行され、React からは useAction で呼び出される 'use server' 関数。
React 19 スタイルのサーバーアクションです — useActionState と同じ形です。'use server' 関数は、実際にはブラウザではなく Node 上で実行されます。
アクションを定義する
'use server'
import { defineAction } from 'murasaki'
import type { ActionState } from 'murasaki'
export const greet = defineAction(
async (_prev: ActionState<string>, formData: FormData): Promise<ActionState<string>> => {
const name = formData.get('name')
return { data: `Hello, ${name}!`, error: null, isPending: false }
},
)defineAction は型付きのパススルーです — 'use server' のセマンティクスが
TypeScript を通じて伝わるようにするためだけに存在します。実際のコード分割は、そのディ
レクティブを検出する Vite プラグインの中で行われます。
ページから呼び出す
import { useAction } from 'murasaki'
import { greet } from '../actions'
export default function Home() {
const [state, run, isPending] = useAction(greet, {
data: null,
error: null,
isPending: false,
})
return (
<form action={run}>
<input name="name" />
<button disabled={isPending}>Greet</button>
{state.data && <p>{state.data}</p>}
</form>
)
}useAction は React 19 の useActionState を直接ラップしているため、[state, run, isPending] は Next.js ですでにおなじみの形そのままです。<form> の外(例えばボタンの
onClick など)からアクションを呼び出すには、直接呼び出します —
callAction(fn, ...args) はそのための薄いパススルーです。
実行の仕組み
Vite プラグインは 'use server' モジュールを2つに分割します。クライアントバンドルに
は型付きの fetch スタブ(fetch('/__murasaki/action/…'))が入り、関数の本体はサー
バー側に残ります。
- 開発時 — Vite ミドルウェアがスタブのリクエストを受け取り、
ssrLoadModule経由で 実際の関数を呼び出します。 - 本番時 —
murasaki bundleが自己完結型のアクションレジストリ (dist/server)をコンパイルし、(ネイティブランチャーによって起動される)バンドル 済みの Node 子サーバーが、同じ/__murasaki/action/…リクエストをそれに対して処理し ます。
いずれの場合も、アクション本体そのものがクライアントに配信されることはなく、スタブだけ が配信されます。
'use server' ファイルは src/ 配下のどこにでも置けます(src/actions.ts が一般的
な慣習です)— src/app/ や src/api/ に紐づく必要はありません。