Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    blencorp

    tanstack-router

    blencorp/tanstack-router
    Coding
    60
    8 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    TanStack Router file-based routing patterns including route creation, navigation, loaders, type-safe routing, and lazy loading...

    SKILL.md

    TanStack Router Patterns

    Purpose

    File-based routing with TanStack Router, emphasizing type-safe navigation, route loaders, and lazy loading.

    When to Use This Skill

    • Creating new routes
    • Implementing navigation
    • Using route loaders for data
    • Type-safe routing with parameters
    • Lazy loading routes

    Quick Start

    Basic Route

    // routes/posts/index.tsx
    import { createFileRoute } from '@tanstack/react-router';
    import { postsApi } from '~/features/posts/api/postsApi';
    
    export const Route = createFileRoute('/posts')({
      loader: async () => {
        const posts = await postsApi.getAll();
        return { posts };
      },
      component: PostsPage,
    });
    
    function PostsPage() {
      const { posts } = Route.useLoaderData();
    
      return (
        <div>
          <h1>Posts</h1>
          {posts.map(post => (
            <PostCard key={post.id} post={post} />
          ))}
        </div>
      );
    }
    

    File-Based Routing

    Directory Structure

    routes/
    ├── __root.tsx          # Root route
    ├── index.tsx           # /
    ├── about.tsx           # /about
    ├── posts/
    │   ├── index.tsx       # /posts
    │   └── $postId.tsx     # /posts/:postId
    └── users/
        ├── index.tsx       # /users
        └── $userId/
            ├── index.tsx   # /users/:userId
            └── posts.tsx   # /users/:userId/posts
    

    Route Mapping

    File Path                        → URL Path
    routes/index.tsx                 → /
    routes/about.tsx                 → /about
    routes/posts/index.tsx           → /posts
    routes/posts/$postId.tsx         → /posts/:postId
    routes/users/$userId/index.tsx   → /users/:userId
    routes/users/$userId/posts.tsx   → /users/:userId/posts
    

    Route Parameters

    Dynamic Routes

    // routes/posts/$postId.tsx
    import { createFileRoute } from '@tanstack/react-router';
    import { postsApi } from '~/features/posts/api/postsApi';
    
    export const Route = createFileRoute('/posts/$postId')({
      loader: async ({ params }) => {
        const post = await postsApi.get(params.postId);
        return { post };
      },
      component: PostDetails,
    });
    
    function PostDetails() {
      const { post } = Route.useLoaderData();
      const { postId } = Route.useParams();
    
      return (
        <div>
          <h1>{post.title}</h1>
          <p>{post.content}</p>
        </div>
      );
    }
    

    Multiple Parameters

    // routes/users/$userId/posts/$postId.tsx
    export const Route = createFileRoute('/users/$userId/posts/$postId')({
      loader: async ({ params }) => {
        const { userId, postId } = params;
        const post = await postsApi.getByUserAndId(userId, postId);
        return { post };
      },
      component: UserPostDetails,
    });
    

    Route Loaders

    Basic Loader

    export const Route = createFileRoute('/posts')({
      loader: async () => {
        const posts = await postsApi.getAll();
        return { posts };
      },
      component: PostsPage,
    });
    

    Loader with Dependencies

    export const Route = createFileRoute('/users/$userId/posts')({
      loader: async ({ params, context }) => {
        const [user, posts] = await Promise.all([
          userApi.get(params.userId),
          postsApi.getByUser(params.userId),
        ]);
        return { user, posts };
      },
      component: UserPosts,
    });
    

    Loader Error Handling

    export const Route = createFileRoute('/posts/$postId')({
      loader: async ({ params }) => {
        try {
          const post = await postsApi.get(params.postId);
          return { post, error: null };
        } catch (error) {
          return { post: null, error: 'Post not found' };
        }
      },
      component: PostDetails,
    });
    
    function PostDetails() {
      const { post, error } = Route.useLoaderData();
    
      if (error) return <Error message={error} />;
      return <div>{post.title}</div>;
    }
    

    Navigation

    import { Link, useNavigate } from '@tanstack/react-router';
    
    // Link component
    <Link to="/posts/$postId" params={{ postId: '123' }}>View Post</Link>
    
    // Programmatic navigation
    const navigate = useNavigate();
    navigate({ to: '/posts', search: { filter: 'published' } });
    

    Lazy Loading

    Lazy Route Component

    // routes/posts/index.tsx
    import { createFileRoute } from '@tanstack/react-router';
    import { lazy } from 'react';
    
    const PostsPage = lazy(() => import('~/features/posts/PostsPage'));
    
    export const Route = createFileRoute('/posts')({
      component: PostsPage,
    });
    

    Lazy Loader

    export const Route = createFileRoute('/posts')({
      loader: async () => {
        // Dynamically import heavy module only when route loads
        const { processData } = await import('~/lib/heavyModule');
        const posts = await postsApi.getAll();
        const processed = processData(posts);
        return { posts: processed };
      },
      component: PostsPage,
    });
    

    Search Params

    Type-Safe Search Params

    import { z } from 'zod';
    
    const postsSearchSchema = z.object({
      filter: z.enum(['all', 'published', 'draft']).default('all'),
      sort: z.enum(['date', 'title']).default('date'),
      page: z.number().default(1),
    });
    
    export const Route = createFileRoute('/posts')({
      validateSearch: postsSearchSchema,
      loader: async ({ search }) => {
        const posts = await postsApi.getAll(search);
        return { posts };
      },
      component: PostsPage,
    });
    
    function PostsPage() {
      const { posts } = Route.useLoaderData();
      const search = Route.useSearch();
    
      return (
        <div>
          <p>Filter: {search.filter}</p>
          <p>Sort: {search.sort}</p>
          <p>Page: {search.page}</p>
        </div>
      );
    }
    

    Updating Search Params

    import { useNavigate } from '@tanstack/react-router';
    
    function FilterButtons() {
      const navigate = useNavigate();
      const search = Route.useSearch();
    
      const setFilter = (filter: string) => {
        navigate({
          to: '.',
          search: (prev) => ({ ...prev, filter }),
        });
      };
    
      return (
        <div>
          <button onClick={() => setFilter('all')}>All</button>
          <button onClick={() => setFilter('published')}>Published</button>
          <button onClick={() => setFilter('draft')}>Draft</button>
        </div>
      );
    }
    

    Layouts

    Root Layout

    // routes/__root.tsx
    import { createRootRoute, Outlet } from '@tanstack/react-router';
    
    export const Route = createRootRoute({
      component: RootLayout,
    });
    
    function RootLayout() {
      return (
        <div>
          <Header />
          <main>
            <Outlet />  {/* Child routes render here */}
          </main>
          <Footer />
        </div>
      );
    }
    

    Nested Layouts

    // routes/dashboard.tsx
    export const Route = createFileRoute('/dashboard')({
      component: DashboardLayout,
    });
    
    function DashboardLayout() {
      return (
        <div className="dashboard">
          <Sidebar />
          <div className="content">
            <Outlet />  {/* Dashboard child routes */}
          </div>
        </div>
      );
    }
    
    // routes/dashboard/index.tsx
    export const Route = createFileRoute('/dashboard')({
      component: DashboardHome,
    });
    
    // routes/dashboard/analytics.tsx
    export const Route = createFileRoute('/dashboard/analytics')({
      component: Analytics,
    });
    

    Route Guards

    Authentication Guard

    export const Route = createFileRoute('/admin')({
      beforeLoad: async ({ context }) => {
        if (!context.auth.isAuthenticated) {
          throw redirect({
            to: '/login',
            search: { redirect: '/admin' },
          });
        }
      },
      component: AdminPage,
    });
    

    Permission Guard

    export const Route = createFileRoute('/admin/users')({
      beforeLoad: async ({ context }) => {
        if (!context.auth.hasPermission('users:manage')) {
          throw redirect({ to: '/unauthorized' });
        }
      },
      component: UsersPage,
    });
    

    Breadcrumbs

    Route Breadcrumbs

    export const Route = createFileRoute('/posts/$postId')({
      loader: async ({ params }) => {
        const post = await postsApi.get(params.postId);
        return { post };
      },
      meta: ({ loaderData }) => [
        { title: 'Home', path: '/' },
        { title: 'Posts', path: '/posts' },
        { title: loaderData.post.title, path: `/posts/${loaderData.post.id}` },
      ],
      component: PostDetails,
    });
    

    Best Practices

    1. Use Loaders for Data

    // ✅ Good: Loader fetches data
    export const Route = createFileRoute('/posts')({
      loader: async () => {
        const posts = await postsApi.getAll();
        return { posts };
      },
      component: PostsPage,
    });
    
    // ❌ Avoid: Fetching in component
    function PostsPage() {
      const [posts, setPosts] = useState([]);
    
      useEffect(() => {
        postsApi.getAll().then(setPosts);
      }, []);
    
      return <div>...</div>;
    }
    

    2. Lazy Load Heavy Routes

    // ✅ Good: Lazy load admin panel
    const AdminPanel = lazy(() => import('~/features/admin/AdminPanel'));
    
    export const Route = createFileRoute('/admin')({
      component: AdminPanel,
    });
    

    3. Type-Safe Navigation

    // ✅ Good: Type-safe Link
    <Link to="/posts/$postId" params={{ postId: post.id }}>
      View Post
    </Link>
    
    // ❌ Avoid: String concatenation
    <a href={`/posts/${post.id}`}>View Post</a>
    

    Additional Resources

    For more patterns, see:

    • routing-guide.md - Advanced routing
    • navigation-patterns.md - Navigation strategies
    • route-loaders.md - Complex loaders
    Recommended Servers
    ThinAir Geo
    ThinAir Geo
    Svelte
    Svelte
    Vercel Grep
    Vercel Grep
    Repository
    blencorp/claude-code-kit
    Files