Slide 1

Slide 1 text

RSCの時代にReactとフレームワーク の境界を探る 2025-09-06 フロントエンドカンファレンス北海道2025

Slide 2

Slide 2 text

発表者紹介 uhyo 株式会社カオナビ フロントエンドエキスパート ぎりぎりReactのクラスコンポーネントを 使ったことがある。 2

Slide 3

Slide 3 text

React Server Components (RSC) React 19で安定化されたReactの機能。 従来のコンポーネントよりも前の別段階で レンダリングされる。 別段階は、リクエスト時のサーバー側だったり、 ビルド時だったりする。 3

Slide 4

Slide 4 text

RSCを使う方法 RSCはReact用フレームワークと一緒に使用する。 •Next.js RSC対応の先駆者、webpackベースの重量級 •@lazarv/react-server Viteベースの軽量フレームワーク •React Router これもViteベース(現在はプレビュー機能) •Waku やっぱりViteベースのミニマルなフレームワーク など 4

Slide 5

Slide 5 text

疑問 フレームワークを介さないと使えないのに、 RSCは本当にReactの機能なの? フレームワークとRSCの境界はどこにあるの? 5

Slide 6

Slide 6 text

答え① 複数の異なるフレームワークで利用できるのは、 RSCがReact本体の機能だからですよね。 でも、 RSCのやりたいことを実現するために フレームワークの助けが必要だから こうなっています。 6

Slide 7

Slide 7 text

This Talk RSCのやりたいこととはどのようなことで、 フレームワークがそれをどう助けているのかを 解説します。 7

Slide 8

Slide 8 text

でもフレームワーク無しで RSCを動かす 8

Slide 9

Slide 9 text

フレームワーク無しでRSCを動かす RSCを“動かす”だけなら、フレームワーク無しで できる。 しかし、我々が良く知るRSCとは別物になる。 ←このZenn本で詳しく解説しています 9

Slide 10

Slide 10 text

我々が良く知るRSC 10 import { Client } from “./client.tsx”; export const SC = () => { return (
); }; server.tsx “use client”; export const Client = () => { return (

client!

); }; client.tsx import サーバーからクライアントをimportしてコンポーネントを使用できる。

Slide 11

Slide 11 text

フレームワーク無しのRSC 11 const Client = { /* 省略 */ }; export const SC = () => { return (
); }; server.tsx export const Client = () => { return (

client!

); }; client.tsx 独立 サーバーとクライアントは独立した、別々のプログラムになる。

Slide 12

Slide 12 text

フレームワーク無しのRSC 12 const Client = { /* 省略 */ }; export const SC = () => { return (
); }; server.tsx server.tsxはサーバー側用のReactランタイムで 実行。Clientはサーバー側でレンダリングしない。 Clientは「Clientというコンポーネント」の ままクライアント側に送られる。 クライアント側がClientのレンダリングを 担当。

Slide 13

Slide 13 text

フレームワーク無しのRSC 13 export const Client = () => { return (

client!

); }; client.tsx client.tsxはクライアント用のReactランタイムで 実行。 SC部分がただのHTMLとなった状態で クライアント側のランタイムに渡される。 こちらはClientの定義を知っており、 Clientをレンダリングできる。

Slide 14

Slide 14 text

フレームワーク無しのRSCの問題 14 const Client = { /* 省略 */ }; export const SC = () => { return (
); }; server.tsx export const Client = () => { return (

client!

); }; client.tsx 別々のプログラムで2つの定義を同期させる必要がある。非常にDX悪い。

Slide 15

Slide 15 text

フレームワークがやっている こと 15

Slide 16

Slide 16 text

RSCでフレームワークがやっていること importでサーバーとクライアントが繋がった Reactプログラムを、 「サーバー側用」と「クライアント用」の 2つの別々のプログラムへとビルドする。 ビルド サーバー 用 クライ アント用

Slide 17

Slide 17 text

RSCでフレームワークがやっていること このようなビルド工程はバンドラがやっている ことの応用である。 そのため、フレームワークでも、RSC対応の根本 部分はバンドラのプラグインとして実装される。 17

Slide 18

Slide 18 text

Reactフレームワークの構造 18 サーバー用 Reactランタイム クライアント用 Reactランタイム Reactの機能 バンドラの機能 RSC規約に従った サーバー・クライアント分割 及びビルド フレームワーク の個性 ルーティング キャッシュ 最適化 など バンドラプラグイン

Slide 19

Slide 19 text

RSC規約とは “use client” でクライアントの世界への入り口を 示すのがRSCの規約。 Reactフレームワークは同じRSC規約をサポート している。 サーバーとクライアントを「1つのプログラム」に 統合するために必要。 19

Slide 20

Slide 20 text

RSC規約とバンドラの関係 “use client” のようなディレクティブは、 JavaScriptの言語仕様上は意味のない記述。 (”use strict”だけは例外) よって、RSCをサポートするためには バンドラのようなビルドツールが必須。 20

Slide 21

Slide 21 text

バンドラプラグインの実装 RSC規約に基づいた変換例を見てみよう。 今回は @vitejs/plugin-rsc を対象にする。 21 ビルド サーバー 用 クライ アント用

Slide 22

Slide 22 text

サーバー用のビルド方法 サーバー用モジュールからクライアント用ファイ ルをimportする際に、クライアント用モジュール をスタブ的なものに置き換える。 22 置き換え 本当のクライア ント用コードと 切り離される

Slide 23

Slide 23 text

置き換えの実例 置き換え前(実際のクライアント用モジュール) 'use client'; export function Greet({ name }: { name: string }) { return <>Hello {name}; } 23 Waku v0.25.0 のソースコードから一部改変して引用(次のスライドも同じ) https://github.com/wakujs/waku/blob/473d481f0d926e05c9b0b8af5d2b777ff47eb70b/packages /waku/tests/vite-plugin-rsc-transform-internals.test.ts

Slide 24

Slide 24 text

置き換えの実例 置き換え後(サーバー側から見える同モジュール) import { registerClientReference } from 'react-server-dom-webpack/server.edge’; export const Greet = registerClientReference(()=>{ throw new Error('It is not possible to invoke a client function from the server: /src/App.tsx#Greet'); }, '/src/App.tsx', 'Greet'); 24

Slide 25

Slide 25 text

置き換えで何をやっているか 実際のクライアント側モジュールでexportされていた ものに対応するクライアントへの参照を、React本体の 機能として用意されているregisterClientReferenceを 使って用意している。 (クライアントへの参照の実態はファイル名とexport名) 25 本物ではなく参照

Slide 26

Slide 26 text

置き換えによるRSCの動作 Reactのサーバー側ランタイムは クライアントへの参照を認識し、残したままで レンダリングを行う。 クライアント側では、残された参照を解決することが でき、レンダリングの続きを行うことができる。 26

Slide 27

Slide 27 text

バンドラプラグインの実装 @vitejs/plugin-rscのソースコードを見る。 Wakuからも使用されている、Vite公式のRSC用 プラグイン。 引用元: https://github.com/vitejs/vite-plugin- react/blob/b81bf6ac8a273855c5e9f39d71a32d76fd31b61c/packages/plugin-rsc/src/plugin.ts 27

Slide 28

Slide 28 text

バンドラプラグインの実装① { name: 'rsc:use-client', async transform(code, id) { /* 中略 */ const ast = await parseAstAsync(code) if (!hasDirective(ast.body, 'use client')) { delete manager.clientReferenceMetaMap[id] return } /* 以下略 */ 28 ‘use client’ を持つ ファイルが変換対象

Slide 29

Slide 29 text

バンドラプラグインの実装② const result = transformDirectiveProxyExport_(ast, { /* 省略 */ runtime: (name, meta) => { let proxyValue = /* 省略 */ return ( `$$ReactServer.registerClientReference(` + ` ${proxyValue},` + ` ${JSON.stringify(referenceKey)},` + ` ${JSON.stringify(name)})` ) }, 29 各exportを registerClientRefere nce呼び出しに変換

Slide 30

Slide 30 text

実装を見て分かったこと プラグインは、“use client”を頼りに クライアントモジュールをサーバー向けに変換し、 サーバー用のビルドを行う。 バンドラはサーバーとクライアントの境目だけ変換 すればいいため、バンドラに寄り添った規約に なっている。 (実際は開発サーバーとか色々考慮して変換している) 30

Slide 31

Slide 31 text

気になる人は 探せば“use server”に関連した変換などもある。 実装の側からRSCについて理解したい人は、 プラグインの実装を読んでみよう。 31

Slide 32

Slide 32 text

結局、Reactとフレーム ワークの境目はどこ? 32

Slide 33

Slide 33 text

疑問(再掲) フレームワークを介さないと使えないのに、 RSCは本当にReactの機能なの? フレームワークとRSCの境界はどこにあるの? 33

Slide 34

Slide 34 text

Reactフレームワークの構造(再掲) 34 サーバー用 Reactランタイム クライアント用 Reactランタイム Reactの機能 バンドラの機能 RSC規約に従った サーバー・クライアント分割 及びビルド フレームワーク の個性 ルーティング キャッシュ 最適化 など バンドラプラグイン

Slide 35

Slide 35 text

答え②: 境目はこんな感じ 35 サーバー用 Reactランタイム クライアント用 Reactランタイム Reactの機能 バンドラの機能 サーバー・クライアント分割とビルド RSC規約 フレームワーク の個性 ルーティング キャッシュ 最適化 など バンドラプラグイン

Slide 36

Slide 36 text

Reactとフレームワークの境目 Reactは、ランタイムのAPIを提供する。 フレームワークは、ReactのAPIを使って サーバーとクライアントを実装する。 加えて、ReactはRSC規約を定義する。 その規約を実装するのはフレームワーク。

Slide 37

Slide 37 text

ポイント RSC規約は、定義する主体と実装する主体が 異なる。 React側で規約を定義している理由は、おそらく フレームワークを問わず共通の開発体験を保証 するためだろう。 (同じWeb標準を複数ブラウザが実装しているのに近いかも)

Slide 38

Slide 38 text

まとめ RSCは、”use client”のような規約をReactが 規定しフレームワークが実装している。 実際に、Next.js、@vitejs/plugin-rsc、 @lazarv/react-serverなどがRSC規約を実装し、 RSCをサポートしている。 RSC規約は通常、バンドラプラグインとして実装 される。 38