use server / use client のような“魔法の文字列”に頼らず、createServerFn でサーバー境界を明示するcreateCompositeComponent により、サーバーUIにクライアント要素を slot で差し込めるuse client が完全不要というわけではないが、依存をかなり減らせるのがポイントReact Server Components(RSC)は、ざっくり言うと「ブラウザではなくサーバー側で動く React コンポーネント」です。
普通の React コンポーネントは JavaScript としてブラウザに送られますが、RSC は 結果だけ がブラウザに届きます。なので、重い処理をブラウザに持ち込まずに済む。これはかなり気持ちいい発想です。
元記事では、この RSC の世界で TanStack Start がかなり変わったアプローチを取っている と紹介しています。
一言でいうと、TanStack Start は RSC を「描画するもの」ではなく「データとして受け渡すもの」 と捉えている、という話です。ここがいちばん面白いところだと思います。
Next.js など、RSC をサポートする多くのフレームワークは、まずサーバー側でページ全体の構造を組み立てます。
そのうえで、ボタンやフォームのようなインタラクティブな部分だけを 'use client' で切り出す、という作りです。
これはわかりやすい反面、アプリ全体が RSC 前提 になりやすい。
つまり、どこで RSC を作るか、どこでクライアントに渡すか、どこを境界にするかを、フレームワークがかなり強く決めてしまうんですね。
個人的には、これは便利な半面、少し“フレームワークに考えさせすぎる”感じもあります。
もちろんそれが良い場面も多いのですが、「自分で境界をちゃんと見たい」という人には息苦しいこともあると思います。

TanStack Start の発想はかなり独特です。
RSC を HTTP でやりとりするデータ として扱います。ここで出てくるのが React Flight です。
React Flight は、React のコンポーネントツリーをネットワーク越しに運ぶための専用フォーマットです。
元記事では、JSON や HTML と比較して説明されていました。
これ、地味に革命的です。
普通の JSON フェッチだと、日付整形、Markdown 変換、シンタックスハイライトみたいな処理は、最終的にクライアントでも動く JS が必要になります。
でも Flight なら、そういう重い処理をサーバー側だけで済ませやすい。しかも ストリーミング で少しずつ流せるので、全部終わるのを待たずに表示を始められる。これはかなり実用的です。
TanStack Start の良さは、RSC を特別な聖域に閉じ込めないことです。
![]()
元記事では、次のような特徴が挙げられていました。
ここがかなり TanStack っぽいです。
つまり、「RSC 専用の新しいやり方を覚えてください」ではなく、いつものデータ取得の延長で扱っていいよ という態度なんですよね。
これはかなり好感が持てます。新しい概念が増えると、それだけで開発者の脳みそは疲れるので……。
「RSC のための特別ルール」を増やさない設計は、長期的に見て強いと思います。
TanStack Start は、Next.js とは逆に クライアントファースト の構成を取ります。
クライアント側の Router がツリーを持ち、RSC はその中に流し込まれるデータとして扱われます。
元記事の図を言い換えると、

という感じです。
この違いはかなり大きいです。
TanStack Start では、JSON を fetch するのと同じ感覚で RSC を取得・更新できます。
「RSC だから特別」というより、「いつものデータ層に乗っている」という感覚に近い。
個人的には、この割り切りはかなり現代的だと思います。
アプリの主導権をクライアント側に置きつつ、重い処理だけサーバーに逃がす。実に合理的です。
use server を使わず、createServerFn で境界を明示するTanStack Start は 'use server' をサポートしていません。
つまり、ファイルの先頭に文字列を置くだけで「ここからサーバーです」とする方式は採らない、ということです。
これには賛否ありそうですが、元記事の指摘はかなり筋が通っています。
'use server' のようなディレクティブは便利な一方で、

がコードを見ただけでは追いにくいことがあるんですよね。
TanStack Start では、代わりに createServerFn を使います。
import { createServerFn } from '@tanstack/react-start'
export const createPost = createServerFn({ method: 'POST' })
.validator(schema)
.handler(async ({ data }) => { ... })
これはかなり親切です。
method が見えるし、validator があるかも見えるし、handler が実処理だと一目でわかる。
つまり、魔法ではなく設計として境界が読める んです。
私はこういう「コードを読んだ瞬間に挙動がわかる」設計が好きです。
便利さよりも、まず可読性。これは大事だと思います。
createCompositeComponent が合成の肝RSC の話で難しいのは、サーバーが描いた UI に、どうやってクライアントのボタンやフォームを差し込むか、という点です。
TanStack Start では、ここを Composite Components で解決します。

これは、サーバー側が UI の骨格を作り、slot のような差し込み口を開けておいて、クライアントがそこへ好きなコンポーネントを流し込める仕組みです。
たとえば、記事では children を slot として使う例が紹介されています。
Counter や LikeButton を差し込むこの構造のいいところは、サーバーが見た目の責任を持ちつつ、インタラクションはクライアントに任せられる ところです。
しかも、クライアント側がツリーを所有しているので、場合によっては 'use client' が不要になることもある。
ただし、ここで重要なのは「完全に不要」ではないことです。
元記事でも触れられていましたが、サーバーが直接クライアントコンポーネントをツリーに配置するケースでは use client が必要になる場面があります。
つまり TanStack Start は、「ディレクティブを全廃する」のではなく、必要最小限に抑える という立場ですね。
この姿勢はかなり現実的です。
理想論で「全部なくせる」と言い切らないのが、むしろ信頼できます。

元記事の後半では、実際のコードを使って TanStack Start の RSC 実装パターンを 14 個紹介しています。
本文で確認できた範囲でも、かなり丁寧に「どう組むか」が説明されています。
RSC を有効にするには、@vitejs/plugin-rsc と TanStack Start の rsc.enabled オプションが必要です。
Vite の設定に組み込んで、RSC を使える状態にします。
また、データ取得には ky、ダミーデータには dummyjson を使っていました。
このあたりは実験用サンプルとしてわかりやすい構成です。
renderServerComponent を使って、サーバーコンポーネントを描画し、その結果を Loader 経由でそのまま使うパターンです。
これはいちばん素直で、「まず RSC を表示してみたい」 ときの基本形です。
スロットが不要なら、これでかなりシンプルに書けます。
createCompositeComponent を使って、サーバーの UI 骨格に children を差し込むパターンです。
Counter や LikeButton のようなインタラクティブ要素を埋め込めるのがポイントです。
これはかなり実践的です。
「見た目はサーバーで、操作はクライアントで」という分担がきれいにできます。

サーバー側が関数を props として受け取り、クライアント側がデータを受けながら UI を描く形です。
いわゆる render props の考え方を RSC 合成に持ち込んでいます。
ここまで来ると、単なる「画面を返す」ではなく、UI の組み立て方そのものを設計している 感じがします。
TanStack Start の面白さは、まさにこの“UI 合成の自由度”にあると思います。
正直、TanStack Start の RSC にはかなり新鮮さがあります。
React の世界ではどうしても「サーバー中心に寄せるか、クライアント中心に寄せるか」で議論が起きがちですが、TanStack Start は クライアントを主役にしたまま、必要なときだけサーバーの力を使う 方向に振っています。
この考え方は、今のフロントエンド開発にかなり合っている気がします。
アプリは結局、ユーザー操作が中心です。だったら、クライアントを主軸に置くのは自然ですし、重い処理や安全性が必要な部分だけサーバーに逃がすのも理にかなっています。
もちろん、Next.js のようなサーバーファーストの設計が不要になるわけではありません。
むしろ、それぞれ向いている場面が違うのだと思います。
でも TanStack Start の「RSC はデータである」という切り口は、RSC を難しく感じていた人ほど刺さるのではないでしょうか。
TanStack Start の RSC は、
「RSC を特別な構文で囲い込む」のではなく、「普通のデータとして扱う」 のが最大の特徴です。
その結果、
use server / use client の依存を減らせるというメリットが生まれます。
まだ発展途上の部分もあるはずですが、設計思想としてはかなり筋がいいと思います。
「RSC をどう使うか」ではなく、「RSC をどうデータ基盤に溶かすか」を考える発想。これは今後かなり面白くなりそうです。
参考: “use server” も “use client” も要らない —TanStack Start が示す新しいRSCの形