第6回
TanStack Start v1を試す:型安全フルスタックReact
v1リリース候補になったTanStack Startを実際に試す。プロジェクト作成からファイルベースルーティング、Server Functionsまでを動かしながら解説する。
「React最新ラボ」第6回。フレームワーク編の第2弾として、TanStack Start を実際に動かす。2026年2月の調査ではReact開発者の15%がすでに採用、50%が「試したい」と答えた、いま一番注目されているフレームワークだ。
立ち位置を整理する:Waku・Next.jsとの違い
| Waku(第2回) | TanStack Start | Next.js | |
|---|---|---|---|
| 思想 | 最小のRSCフレームワーク | クライアントファースト+SSR | サーバファースト(RSC中心) |
| RSC | ある(それが本体) | まだない(対応作業中) | ある |
| ルーティング | ファイルベース | ファイルベース(TanStack Router) | ファイルベース(App Router) |
| 型安全 | 普通のTS | ルート・パラメータ・検索クエリまで全部型付き | 部分的 |
| サーバ処理 | Server Functions | Server Functions(createServerFn) |
Server Actions |
プロジェクトを作る
公式CLIから対話形式で作れる。
npx @tanstack/cli@latest createパッケージマネージャや、Tailwind CSS・ESLintなどのアドオンを聞かれるので好みで選ぶ。できたら開発サーバを起動する。
cd my-start-app
npm run dev
# → http://localhost:3000ファイルベースルーティング:src/routes
ルートは src/routes 配下のファイルで定義する。Wakuの src/pages と似ているが、各ファイルで createFileRoute を呼ぶのがStart流だ。
src/
├── routes/
│ ├── __root.tsx # 全ページ共通のルート(レイアウト)
│ ├── index.tsx # /
│ └── posts.$postId.tsx # /posts/:postId(動的パラメータ)
└── router.tsx// ./src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/')({
component: Home,
})
function Home() {
return <h1>TanStack Start を試す</h1>
}面白いのは動的ルートだ。posts.$postId.tsx というファイル名にすると、params が自動で型付きになる。
// ./src/routes/posts.$postId.tsx
import { createFileRoute, Link } from '@tanstack/react-router'
export const Route = createFileRoute('/posts/$postId')({
component: PostPage,
})
function PostPage() {
// params.postId は string として型が通っている。typoはコンパイルエラー
const { postId } = Route.useParams()
return <h1>記事ID: {postId}</h1>
}リンクを張る側も型が効く。存在しないパスや、パラメータの渡し忘れはビルド前にエラーになる。
<Link to="/posts/$postId" params={{ postId: '42' }}>記事42へ</Link>
// to="/psots/$postId" と typo すると即コンパイルエラーServer Functions:サーバ処理を「型付きの関数」として呼ぶ
サーバでだけ実行したい処理は createServerFn で作る。Wakuの 'use server' に相当する仕組みだが、こちらは関数の入出力まで型が通る。
// ./src/server/get-server-time.ts
import { createServerFn } from '@tanstack/react-start'
export const getServerTime = createServerFn().handler(async () => {
// ここはサーバでだけ実行される。DBアクセスや秘密鍵もOK
return new Date().toISOString()
})入力を受け取るときはバリデータを挟む。
export const greetUser = createServerFn({ method: 'GET' })
.validator((data: { name: string }) => data)
.handler(async ({ data }) => {
return `Hello, ${data.name}!` // data.name は string と推論される
})呼び出し側はルートの loader に置くのが基本形。SSR時はサーバ内で直接実行され、クライアント遷移時は自動でHTTP経由になる。呼ぶ側のコードはどちらでも同じだ。
// ./src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'
import { getServerTime } from '../server/get-server-time'
export const Route = createFileRoute('/')({
loader: () => getServerTime(),
component: Home,
})
function Home() {
const time = Route.useLoaderData() // string と推論される
return (
<main>
<h1>TanStack Start を試す</h1>
<p>サーバ時刻: {time}</p>
</main>
)
}TanStack Start を試す
サーバ時刻: 2026-07-19T09:24:07.518Z
デプロイ
StartはViteプラグインとして動くので、ビルドは普通に npm run build。Cloudflare Workers・Netlify・Vercelなど主要ホスティング向けのデプロイターゲットが用意されている。このシリーズの流儀でいくならCloudflareだが、手順はWakuの回とほぼ同じ流れ(アダプタ設定→デプロイ)なので、詳細は公式ドキュメントを参照してほしい。
つまずきやすい所
- RSCは使えない:
asyncなサーバコンポーネントを書くスタイルではない。データ取得はloader+ Server Functions が正解ルート - RC版:v1リリース候補。APIはほぼ確定だが、細部は変わりうる。採用判断は1.0正式リリースを見てから
- ルートの型が更新されない:ルートツリーの型は自動生成される。エディタの型エラーが残るときは、開発サーバが動いているか(生成が走っているか)を確認する
まとめ
- TanStack StartはTanStack Router上のフルスタックフレームワーク。v1 RCまで到達し、APIは安定
- 思想はクライアントファースト。SPAの延長で書けて、SSRとServer Functionsが足される
- ルート・パラメータ・
<Link>・loaderの戻り値まで端から端まで型安全。リンク切れがコンパイルエラーになる - サーバ処理は
createServerFn。SSR時は直接実行、クライアントからは自動でHTTP経由 - RSCは未対応(対応作業中)。RSCが必要ならWakuやNext.jsを選ぶ
React本体(Compiler・Activity)からフレームワークまで、2026年のReactは「書き味はシンプルに、最適化は道具に任せる」方向へ着実に進んでいる。次に話題の技術が出てきたら、また「最新ラボ」で実際に動かして確かめよう。