TanStack Start - フルスタックReactフレームワーク

2024.12.28

公式ドキュメント

TanStack Startとは

TanStack Startは、TanStack RouterをベースにしたフルスタックReactフレームワークです。型安全なルーティング、サーバー関数、SSR/SSGを統合し、Next.jsの代替として注目されています。

特徴

主な特徴:

  • 完全な型安全性
  • ファイルベースルーティング
  • サーバー関数
  • ストリーミングSSR
  • 複数のデプロイターゲット
  • TanStack Query統合

プロジェクトのセットアップ

npm create tanstack-start@latest my-app
cd my-app
npm install
npm run dev

ファイルベースルーティング

ディレクトリ構造:

  • routes/
  • __root.tsx (ルートレイアウト)
  • index.tsx (/)
  • about.tsx (/about)
  • posts/
  • index.tsx (/posts)
  • $postId.tsx (/posts/:postId)
  • new.tsx (/posts/new)
  • _auth/ (レイアウトグループ)
  • login.tsx
  • register.tsx
  • routes.gen.ts (自動生成)

ルート定義

基本的なルート

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

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

function HomePage() {
 return <h1>Welcome to TanStack Start!</h1>;
}

動的ルート

// routes/posts/$postId.tsx
import { createFileRoute } from '@tanstack/react-router';

export const Route = createFileRoute('/posts/$postId')({
 loader: async ({ params }) => {
 // params.postId は型安全
 const post = await fetchPost(params.postId);
 return { post };
 },
 component: PostPage
});

function PostPage() {
 const { post } = Route.useLoaderData();
 return (
 <article>
 <h1>{post.title}</h1>
 <p>{post.content}</p>
 </article>
 );
}

ポイント: TanStack Startのルート定義はcreateFileRouteで型安全に行え、ローダーやメタデータもすべて型付きです

サーバー関数

定義と使用

// app/serverFunctions/posts.ts
import { createServerFn } from '@tanstack/start';
import { z } from 'zod';

export const getPosts = createServerFn('GET', async () => {
 return db.posts.findMany();
});

export const createPost = createServerFn('POST', async (data) => {
 const validated = z.object({
 title: z.string(),
 content: z.string()
 }).parse(data);

 return db.posts.create({ data: validated });
});

クライアントからの呼び出し

// routes/posts/index.tsx
import { getPosts, createPost } from '../serverFunctions/posts';

export const Route = createFileRoute('/posts/')({
 loader: () => getPosts(),
 component: PostsPage
});

function PostsPage() {
 const posts = Route.useLoaderData();

 const handleCreate = async () => {
 await createPost({ title: 'New Post', content: 'Content' });
 };

 return (
 <div>
 {posts.map(post => <PostCard key={post.id} post={post} />)}
 <button onClick={handleCreate}>Create Post</button>
 </div>
 );
}

実践メモ: サーバー関数はcreateServerFnで定義し、'use server'を明示することでサーバーサイドでのみ実行されることが保証されます

データフェッチング

Loaderパターン

export const Route = createFileRoute('/dashboard')({
 // サーバーサイドでデータを取得
 loader: async ({ context }) => {
 const [user, stats] = await Promise.all([
 getUser(context.userId),
 getStats(context.userId)
 ]);
 return { user, stats };
 },
 // ペンディング状態の表示
 pendingComponent: () => <div>Loading...</div>,
 // エラー状態の表示
 errorComponent: ({ error }) => <div>Error: {error.message}</div>,
 component: DashboardPage
});

TanStack Queryとの統合

import { createFileRoute } from '@tanstack/react-router';
import { useSuspenseQuery } from '@tanstack/react-query';

export const Route = createFileRoute('/posts/')({
 component: PostsPage
});

function PostsPage() {
 const { data: posts } = useSuspenseQuery({
 queryKey: ['posts'],
 queryFn: () => fetch('/api/posts').then(r => r.json())
 });

 return <PostList posts={posts} />;
}

レイアウト

// routes/__root.tsx
import { createRootRoute, Outlet } from '@tanstack/react-router';

export const Route = createRootRoute({
 component: RootLayout
});

function RootLayout() {
 return (
 <html>
 <head>
 <meta charSet="utf-8" />
 <title>My App</title>
 </head>
 <body>
 <nav>
 <Link to="/">Home</Link>
 <Link to="/posts">Posts</Link>
 </nav>
 <main>
 <Outlet />
 </main>
 </body>
 </html>
 );
}

型安全なリンク

import { Link } from '@tanstack/react-router';

// 型エラー: パラメータが不足
<Link to="/posts/$postId">Post</Link>

// 正しい使い方
<Link to="/posts/$postId" params={{ postId: '123' }}>
 Post 123
</Link>

// 検索パラメータも型安全
<Link to="/search" search={{ q: 'react', page: 1 }}>
 Search
</Link>

ミドルウェア

// app/middleware.ts
import { createMiddleware } from '@tanstack/start';

export const authMiddleware = createMiddleware({
 beforeLoad: async ({ context }) => {
 const session = await getSession();
 if (!session) {
 throw redirect({ to: '/login' });
 }
 return { ...context, session };
 }
});

// ルートで使用
export const Route = createFileRoute('/dashboard')({
 middleware: [authMiddleware],
 component: Dashboard
});

注意: TanStack Startはまだ比較的新しいフレームワークです。本番環境での利用前にエコシステムの成熟度を確認してください

デプロイ

# Node.js
npm run build
npm start

# Cloudflare Workers
npm run build --preset cloudflare-pages

# Vercel
npm run build --preset vercel

# Netlify
npm run build --preset netlify

まとめ

TanStack Startは、完全な型安全性とモダンなサーバー機能を備えたフルスタックReactフレームワークです。TanStack Routerの強力なルーティングシステムと、シンプルなサーバー関数により、効率的なフルスタック開発が可能です。

参考リソース

← 一覧に戻る
PR
PR
PR
PR