AgentSkillsCN

tanstack-router

TanStack Router 基于文件的路由。适用于路由、参数、查询参数、导航、加载器、认证守卫、布局、嵌套路由、重定向等场景。

SKILL.md
--- frontmatter
name: tanstack-router
description: TanStack Router file-based routing. Use for routes, params, search params, navigation, loaders, auth guards, layouts, nested routes, redirect

TanStack Router

File-Based Routing

sh
routes/
├── __root.tsx        # Root layout (meta, providers)
├── index.tsx         # / (homepage)
├── _app.tsx          # Layout for /_app/* routes
├── _app/
│   ├── index.tsx     # /app
│   └── settings.tsx  # /app/settings
├── posts.tsx         # /posts (list)
├── posts/
│   └── $id.tsx       # /posts/:id (detail)
└── api/
    └── auth/$.tsx    # /api/auth/* (catch-all)

Route Definition

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

export const Route = createFileRoute('/posts/$id')({
  component: PostPage,
  loader: async ({ params }) => fetchPost(params.id),
  validateSearch: z.object({ tab: z.string().optional() }),
  errorComponent: PostError,
  pendingComponent: PostSkeleton,
});

function PostPage() {
  const post = Route.useLoaderData();
  const { tab } = Route.useSearch();
  const { id } = Route.useParams();
  return <Post data={post} />;
}

Route Context Pattern

tsx
// __root.tsx
import { createRootRouteWithContext } from '@tanstack/react-router';

type RouterContext = {
  queryClient: QueryClient;
};

export const Route = createRootRouteWithContext<RouterContext>()({
  component: RootLayout,
});

// router.tsx
export const getRouter = () =>
  createRouter({
    routeTree,
    context: { queryClient },
  });

Auth Guard (beforeLoad)

tsx
export const Route = createFileRoute('/_app')({
  beforeLoad: async ({ context }) => {
    const session = await auth.api.getSession({
      headers: context.request.headers,
    });

    if (!session) {
      throw redirect({ to: '/login' });
    }

    return { user: session.user };
  },
  component: AppLayout,
});

Navigation

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

// Link component
<Link to="/posts/$id" params={{ id: '123' }} search={{ tab: 'comments' }}>
  View Post
</Link>;

// Programmatic navigation
const navigate = useNavigate();
navigate({ to: '/posts', search: { page: 2 } });
navigate({ to: '..', search: (prev) => ({ ...prev }) }); // Relative

Search Params with Zod

tsx
import { zodValidator } from '@tanstack/zod-adapter';
import { stripSearchParams } from '@tanstack/react-router';

const defaults = { page: 1, sort: 'newest' as const };

const searchSchema = z.object({
  page: z.number().default(defaults.page),
  sort: z.enum(['newest', 'oldest']).default(defaults.sort),
  filter: z.string().optional(),
});

export const Route = createFileRoute('/posts')({
  validateSearch: zodValidator(searchSchema),
  search: { middlewares: [stripSearchParams(defaults)] },
  component: PostsPage,
});

function PostsPage() {
  const { page, sort, filter } = Route.useSearch();
  const navigate = useNavigate();

  const setPage = (newPage: number) => {
    navigate({ search: (prev) => ({ ...prev, page: newPage }) });
  };
}

Layout Routes

tsx
// _app.tsx - Pathless layout
export const Route = createFileRoute('/_app')({
  component: AppLayout,
});

function AppLayout() {
  return (
    <div className="flex">
      <Sidebar />
      <main>
        <Outlet /> {/* Child routes render here */}
      </main>
    </div>
  );
}

Loader with TanStack Query

tsx
export const Route = createFileRoute('/posts')({
  loader: async ({ context }) => {
    await context.queryClient.ensureQueryData(postsOptions());
  },
  component: PostsPage,
});

function PostsPage() {
  const { data } = useSuspenseQuery(postsOptions());
  return <PostList posts={data} />;
}

Common Mistakes

MistakeCorrect Pattern
Using validateSearch without adapterUse zodValidator(schema)
Auth check in componentUse beforeLoad for route guards
Not awaiting ensureQueryDataAwait to block navigation until ready
Forgetting Outlet in layoutsLayout components need <Outlet />
Hardcoding paths in navigateUse type-safe to with params
Missing $ prefix for dynamic routesUse $id.tsx not id.tsx
Route params without validationValidate with Zod in loader
Not using search middlewareUse stripSearchParams for clean URLs
Circular redirects in beforeLoadCheck current location before redirect
Not handling 404 in loaderUse throw notFound() for missing data

Delegation

  • Query patterns: For data fetching in loaders, see tanstack-query skill
  • Server functions: For API calls, see tanstack-start skill
  • Auth patterns: For authentication, see auth skill
  • Code review: After implementing routes, delegate to code-reviewer agent

Topic References