Gemcook Tech Blog
🚀

TanStack Startとは?Next.jsに代わるフルスタックReactフレームワークを紹介

に公開

はじめに

みなさん、Reactでフルスタックなwebアプリケーションを開発する際にどのフレームワークを使用していますか?
Next.jsを利用している方が多いかと思いますが、最近注目されているフレームワークとしてTanStack Startがあります。

TanStack Startは、TanStack Query(旧React Query)やTanStack Routerなどを開発しているTanStackチームが提供するフルスタックReactフレームワークです。
ViteとTanStack Routerをベースに構築されており、SSR、ストリーミング、サーバー関数、APIルートなど、モダンなwebアプリケーション開発に必要な機能を備えています。

https://tanstack.com/start/latest

本記事では、TanStack Startの主な特徴や使い方について紹介します。

TanStack Startの主な特徴

TanStack Startの特徴について以下の5つを紹介します。

1. エンドツーエンドの型安全性

TanStack Startの最大の特徴は、フルスタックでの型安全性です。
ルーティング、サーバー関数、ローダー、検索パラメータなど、クライアントからサーバーまですべてがTypeScriptで型付けされています。

例えば、ルートパラメータや検索パラメータに対して自動的に型推論が行われるため、params.idの型がわからないといった問題が発生しません。

2. サーバー関数(Server Functions)

サーバー関数は、クライアントやサーバーのどこからでも呼び出すことができ、常にサーバー上で実行される関数です。
データベースへのアクセスや環境変数の参照など、サーバーでのみ実行したい処理を型安全に記述することができます。

import { createServerFn } from '@tanstack/react-start'

export const getServerTime = createServerFn().handler(async () => {
  return new Date().toISOString()
})

const time = await getServerTime()

入力バリデーションやミドルウェアもサポートしており、以下のように記述できます。

import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
import { zodValidator } from '@tanstack/zod-adapter'

export const getTodos = createServerFn({ method: 'GET' })
  .validator(zodValidator(z.object({ userId: z.string() })))
  .middleware([authMiddleware])
  .handler(async ({ data, context }) => {
    return db.todos.findMany({ where: { userId: data.userId } })
  })

3. Viteベースの高速な開発体験

TanStack StartはViteをベースにしているため、以下のメリットがあります。

  • 高速なHMR(Hot Module Replacement): コードの変更が即座に反映される
  • 軽量なリソース使用量: 開発サーバーのCPU・メモリ使用量が少ない
  • 高速な起動: 開発サーバーの起動が早い

Next.jsのWebpack/Turbopackと比較して、開発体験が軽快に感じられるポイントです。

4. SSR・ストリーミング・静的プリレンダリング

TanStack Startはさまざまなレンダリング方式をサポートしています。

  • フルドキュメントSSR: サーバーサイドレンダリングによるSEOやパフォーマンスの向上
  • ストリーミングSSR: HTMLを準備でき次第クライアントに送信し、段階的にページを表示
  • 静的プリレンダリング(SSG): ビルド時にHTMLを事前生成
  • ISR(Incremental Static Regeneration): Cache-Controlヘッダーを利用した段階的な静的再生成
  • SPAモード: クライアントサイドのみで動作するSPAとしての利用
  • 選択的SSR: ルートごとにSSRの挙動を細かく制御可能

特に選択的SSR(Selective SSR) は、ルートごとにフルSSR、データのみ、クライアントのみといった細かい制御ができるTanStack Startの強みの1つです。

5. デプロイ先の柔軟性

ViteベースであるTanStack Startは、特定のホスティングプロバイダーに依存しません。
以下のようなさまざまなプラットフォームにデプロイすることができます。

  • Cloudflare Workers
  • Netlify
  • Vercel
  • Node.js / Bun
  • Docker

Next.jsがVercelに最適化されているのに対して、TanStack Startはどこにでもデプロイできる柔軟性を持っています。

プロジェクトの始め方

TanStack Startのプロジェクトを始める方法について紹介します。

CLIで始める

最も簡単な方法は、CLIを使用することです。

npm create @tanstack/start@latest

または、pnpmの場合は以下になります。

pnpm create @tanstack/start@latest

CLIの対話プロンプトについて

CLIを実行すると、対話形式でプロジェクトの設定を行うことができます。
主に以下の項目について質問されます。

1. プロジェクト名

作成するプロジェクトのディレクトリ名を入力します。

2. パッケージマネージャー

使用するパッケージマネージャーを選択します。

  • npm, pnpm, yarn, bun, deno

3. ツールチェーン(Toolchain)

フォーマッターやリンターの設定を選択します。

  • Biome: biome.jsonが追加され、Biomeがフォーマッターおよびリンターとして設定される
  • ESLint + Prettier: eslint.config.jsprettier.config.js.prettierignoreが追加される
  • None: ツールチェーンを追加しない

4. アドオン(Add-ons)

プロジェクトに追加する機能・ライブラリを選択します。
主なアドオンとしては以下のようなものがあります。

カテゴリ アドオン例
データフェッチング TanStack Query
UI shadcn/ui
認証 Clerk
データベース Drizzle
フォーム TanStack Form

アドオンを選択すると、必要な依存関係のインストールや設定ファイルの生成、デモ用のルートの作成まで自動で行ってくれます。

テンプレートから始める

公式で用意されているテンプレートをクローンして始めることも可能です。

npx gitpick TanStack/router/tree/main/examples/react/start-basic start-basic
cd start-basic
npm install
npm run dev

上記のコマンドを実行すると、基本的なTanStack Startのプロジェクトが立ち上がります。
start-basicの部分を変更することで、認証付きやデータベース接続付きなど、さまざまなテンプレートを利用することができます。

テンプレートの一覧については以下の公式ドキュメントで確認してください。

https://tanstack.com/start/latest/docs/framework/react/quick-start

スクラッチで構築する

手動でプロジェクトを構築する場合は、以下の手順で行います。

mkdir myApp
cd myApp
npm init -y

必要なパッケージをインストールします。

npm i @tanstack/react-start @tanstack/react-router react react-dom
npm i -D vite @vitejs/plugin-react typescript @types/react @types/react-dom

tsconfig.jsonを作成します。

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "moduleResolution": "Bundler",
    "module": "ESNext",
    "target": "ES2022",
    "skipLibCheck": true,
    "strictNullChecks": true
  }
}

スクラッチで構築する場合の詳細な手順については、公式ドキュメントを参照してください。

https://tanstack.com/start/latest/docs/framework/react/build-from-scratch

プロジェクト構成

CLIやテンプレートでセットアップした後のプロジェクト構成は以下のようになっています。

my-tanstack-app/
├── src/
│   ├── routes/              # ファイルベースのルーティング
│   │   ├── __root.tsx       # ルートレイアウト(全ページ共通のレイアウト)
│   │   └── index.tsx        # トップページ( / )
│   ├── styles/
│   │   └── app.css          # グローバルスタイル
│   ├── router.tsx           # ルーターの設定
│   └── routeTree.gen.ts     # 自動生成されるルートツリー(手動編集不要)
├── app.config.ts            # TanStack Startの設定ファイル
├── tsconfig.json
└── package.json

各ファイル・ディレクトリの役割について補足します。

  • src/routes/: ファイルベースルーティングのディレクトリです。このディレクトリ内にファイルを追加することで、自動的にルートが生成されます。
  • src/routes/__root.tsx: すべてのページに適用されるルートレイアウトです。<html><head>タグ、共通のナビゲーションなどをここに記述します。
  • src/routeTree.gen.ts: ルートの定義から自動生成されるファイルです。ここにルートの型情報が含まれており、型安全なナビゲーションの基盤になっています。このファイルは手動で編集する必要はありません。
  • app.config.ts: TanStack Startのアプリケーション設定ファイルです。Viteのプラグイン設定やサーバー設定などを記述します。

簡単なページを作ってみる

ここからは、実際にTanStack Startでページを作成する流れを紹介します。

ルートレイアウトの作成

まず、src/routes/__root.tsxにルートレイアウトを作成します。
このファイルはすべてのページで共通して使われるレイアウトを定義します。

// src/routes/__root.tsx
import type { ReactNode } from 'react'
import {
  Outlet,
  createRootRoute,
  HeadContent,
  Scripts,
  Link,
} from '@tanstack/react-router'

export const Route = createRootRoute({
  head: () => ({
    meta: [
      { charSet: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { title: 'TanStack Start App' },
    ],
  }),
  component: RootComponent,
})

function RootComponent() {
  return (
    <RootDocument>
      <Outlet />
    </RootDocument>
  )
}

function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
  return (
    <html lang="ja">
      <head>
        <HeadContent />
      </head>
      <body>
        <header>
          <nav>
            <Link to="/">ホーム</Link>
            <Link to="/about">About</Link>
          </nav>
        </header>
        <main>{children}</main>
        <Scripts />
      </body>
    </html>
  )
}

<Link>コンポーネントのtoプロパティは型安全になっており、存在しないルートを指定するとTypeScriptのエラーが発生します。
この型安全なナビゲーションがTanStack Startの大きな魅力の1つです。

トップページの作成

src/routes/index.tsxにトップページを作成します。

// src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/')({
  component: HomePage,
})

function HomePage() {
  return (
    <div>
      <h1>Welcome to TanStack Start!</h1>
      <p>型安全なフルスタックReact開発を始めましょう</p>
    </div>
  )
}

createFileRouteにルートのパスを渡してルートを定義します。
ファイルを保存すると、routeTree.gen.tsが自動的に更新され、ルーティング情報に反映されます。

Aboutページの追加

新しいページを追加するには、src/routes/ディレクトリにファイルを追加するだけです。

// src/routes/about.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/about')({
  component: AboutPage,
})

function AboutPage() {
  return (
    <div>
      <h1>About</h1>
      <p>TanStack Startで作成したAboutページです</p>
    </div>
  )
}

先ほどルートレイアウトで<Link to="/about">と記述していましたが、このファイルを作成することでリンクが正しく動作するようになります。

サーバー関数を使ったデータ取得

TanStack Startの強力な機能の1つであるサーバー関数を使って、サーバーサイドでのデータ取得を行ってみます。

// src/routes/posts.tsx
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'

// サーバー関数の定義(サーバーサイドで実行される)
const getPosts = createServerFn({ method: 'GET' }).handler(async () => {
  const res = await fetch(
    'https://jsonplaceholder.typicode.com/posts?_limit=10'
  )
  return res.json()
})

export const Route = createFileRoute('/posts')({
  // ルートのローダーでサーバー関数を呼び出し
  loader: () => getPosts(),
  component: PostsPage,
})

function PostsPage() {
  // ローダーのデータを取得(型が自動推論される)
  const posts = Route.useLoaderData()

  return (
    <div>
      <h1>投稿一覧</h1>
      <ul>
        {posts.map((post: { id: number; title: string }) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  )
}

createServerFnで定義した関数はサーバーサイドで実行されますが、クライアントからは通常の関数呼び出しのように利用できます。
ルートのloaderでサーバー関数を使用してデータを取得し、Route.useLoaderData()でコンポーネント内からアクセスする流れです。

コード例: キャッシュの設定

TanStack Routerのキャッシュ機能を利用して、ルートごとにキャッシュの制御を行うことができます。

export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => fetchPost(params.postId),
  staleTime: 10_000,    // 10秒間はキャッシュを新鮮とみなす
  gcTime: 5 * 60_000,   // 5分間メモリに保持
})

さらに、TanStack Queryとの連携も公式にサポートされており、より高度なキャッシュ戦略を実装することも可能です。

Next.jsとの比較

TanStack StartとNext.jsの主な違いについてまとめます。

項目 TanStack Start Next.js
ベースのビルドツール Vite Webpack / Turbopack
ルーティングの型安全性 エンドツーエンドの型推論 手動での型付けが必要
サーバー関数 RPC(型安全、ミドルウェア対応) Server Actions
React Server Components 未対応(開発中) 対応済み
開発サーバーの速度 高速 やや重い
デプロイ先 任意のVite対応ホスト Vercelに最適化
クライアントサイドキャッシュ 組み込みのSWRキャッシュ fetchキャッシュのみ
SPAモード ネイティブ対応 "use client"で対応可能
コミュニティの規模 成長中 大規模

TanStack Startを選ぶと良いケース

  • TanStack QueryやTanStack Routerなど、TanStackのエコシステムをすでに利用している場合
  • エンドツーエンドの型安全性を重視する場合
  • 特定のホスティングプロバイダーに依存したくない場合
  • SPAモードとSSRを柔軟に切り替えたい場合
  • 高速な開発体験(HMR、起動速度)を求める場合

Next.jsを選ぶと良いケース

  • React Server Componentsを利用したい場合
  • 画像の自動最適化やフォントの最適化が必要な場合
  • Vercelにデプロイする予定で、最適化された体験を求める場合
  • 大規模なコミュニティやエコシステムの安定感を重視する場合

現時点での注意点

TanStack Startを利用する際に注意すべきポイントについてもいくつか紹介します。

  • React Server Components(RSC)には未対応: 現在RSCのサポートは開発中で、将来的に対応予定とされています。
  • RC段階: 執筆時点ではRelease Candidateであり、まだ正式なv1リリースには至っていません。ただし、APIは安定しておりプロダクションでの利用も可能とされています。
  • コミュニティの規模: Next.jsと比較するとコミュニティの規模は小さいため、日本語の情報や参考記事が少ないことがあります。

まとめ

TanStack Startは、エンドツーエンドの型安全性Viteベースの高速な開発体験を強みとする、新しいフルスタックReactフレームワークです。

特にTanStack QueryやTanStack Routerなど、TanStackのエコシステムをすでに活用しているプロジェクトでは、スムーズに導入できるのではないでしょうか。
RSCのサポートなど今後のアップデートにも期待しつつ、選択肢の1つとして検討してみてはいかがでしょうか。

さらに詳しく知りたい方は以下の公式ドキュメントやリポジトリを参照してみてください。

https://tanstack.com/start/latest/docs/framework/react/overview

https://github.com/TanStack/router

最後まで読んでいただきありがとうございました!

Gemcook Tech Blog
Gemcook Tech Blog

Discussion