Receber por email
← Voltar para o catálogo

tanstack

Construa aplicativos React com segurança de tipo usando TanStack Query (busca de dados, cache, mutações), Router (roteamento baseado em arquivos, parâmetros de busca, loaders) e Start (SSR, funções de servidor, middleware).

29estrelas
Atualizado há 4 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add tenequm/skills

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/tanstack/svg)](https://www.skillteca.com.br/skills/tanstack)

Alerta por categoria

Receba novas skills de Design e Frontend toda segunda

1 email curto, só com as skills novas de Design e Frontend. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

TanStack (Query + Router + Start)

Type-safe libraries for React applications. Query manages server state (fetching, caching, mutations). Router provides file-based routing with validated search params and data loaders. Start extends Router with SSR, server functions, and middleware for full-stack apps.

When to Use

Query - data fetching, caching, mutations, optimistic updates, infinite scroll, streaming AI/SSE responses, tRPC v11 integration Router - file-based routing, type-safe navigation, validated search params, route loaders, code splitting, preloading Start - SSR/SSG, server functions (type-safe RPCs), middleware, API routes, deployment to Cloudflare/Vercel/Node

Decision tree:

  • Client-only SPA with API calls -> Router + Query
  • Full-stack with SSR/server functions -> Start + Query (Start includes Router)

TanStack Query v5

Setup

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutes
    },
  },
})

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
    </QueryClientProvider>
  )
}

Queries

import { useQuery, queryOptions } from '@tanstack/react-query'

// Reusable query definition (recommended pattern)
const todosQueryOptions = queryOptions({
  queryKey: ['todos'],
  queryFn: async () => {
    const res = await fetch('/api/todos')
    if (!res.ok) throw new Error('Failed to fetch')
    return res.json() as Promise<Todo[]>
  },
})

// In component - full type inference from queryOptions
function TodoList() {
  const { data, isLoading, error } = useQuery(todosQueryOptions)
  if (isLoading) return <Spinner />
  if (error) return <div>Error: {error.message}</div>
  return <ul>{data.map(t => <li key={t.id}>{t.title}</li>)}</ul>
}

Mutations

import { useMutation, useQueryClient } from '@tanstack/react-query'

function CreateTodo() {
  const queryClient = useQueryClient()
  const mutation = useMutation({
    mutationFn: (newTodo: { title: string }) =>
      fetch('/api/todos', { method: 'POST', body: JSON.stringify(newTodo) }).then(r => r.json()),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  })

  return (
    <button onClick={() => mutation.mutate({ title: 'New' })}>
      {mutation.isPending ? 'Creating...' : 'Create'}
    </button>
  )
}

Key Patterns

Query keys - hierarchical arrays for cache management:

['todos']                          // all todos
['todos', 'list', { page, sort }]  // filtered list
['todo', todoId]                   // single item

Dependent queries - chain with enabled:

const { data: user } = useQuery({ queryKey: ['user', id], queryFn: () => fetchUser(id) })
const { data: projects } = useQuery({
  queryKey: ['projects', user?.id],
  queryFn: () => fetchProjects(user!.id),
  enabled: !!user?.id,
})

Important defaults: staleTime: 0, gcTime: 5min, retry: 3, refetchOnWindowFocus: true

Suspense - use useSuspenseQuery with <Suspense> boundaries

Streamed queries (experimental) - for AI chat/SSE:

import { experimental_streamedQuery as streamedQuery } from '@tanstack/react-query'

const { data: chunks } = useQuery(queryOptions({
  queryKey: ['chat', sessionId],
  queryFn: streamedQuery({ streamFn: () => fetchChatStream(sessionId), refetchMode: 'reset' }),
}))

DevTools

pnpm add @tanstack/react-query-devtools
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
// Add inside QueryClientProvider
<ReactQueryDevtools initialIsOpen={false} />

Query Deep Dives

  • query-guide.md - Complete Query reference with all patterns
  • infinite-queries.md - useInfiniteQuery, pagination, virtual scroll
  • optimistic-updates.md - Optimistic UI, rollback, undo
  • query-performance.md - staleTime tuning, deduplication, prefetching
  • query-invalidation.md - Cache invalidation strategies, filters, predicates
  • query-typescript.md - Type inference, generics, custom hooks

TanStack Router v1

Setup (Vite)

pnpm add @tanstack/react-router @tanstack/router-plugin
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({ autoCodeSplitting: true }),
    react(),
  ],
})
// src/router.ts
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

export const router = createRouter({ routeTree, defaultPreload: 'intent' })

declare module '@tanstack/react-router' {
  interface Register { router: typeof router }
}

File-Based Routing

Files in src/routes/ auto-generate route config:

| Convention | Purpose | Example | |---|---|---| | __root.tsx | Root route (always rendered) | src/routes/__root.tsx | | index.tsx | Index route | src/routes/index.tsx -> / | | $param | Dynamic segment | posts.$postId.tsx -> /posts/:id | | _prefix | Pathless layout | _layout.tsx wraps children | | (folder) | Route group (no URL) | (auth)/login.tsx -> /login |

Type-Safe Navigation

<Link to="/posts/$postId" params={{ postId: '123' }}>View Post</Link>

// Active styling
<Link to="/posts" activeProps={{ className: 'font-bold' }}>Posts</Link>

// Imperative
const navigate = useNavigate({ from: '/posts' })
navigate({ to: '/posts/$postId', params: { postId: post.id } })

Always provide from on Link and hooks - narrows types and improves TS performance.

Search Params

import { zodValidator, fallback } from '@tanstack/zod-adapter'
import { z } from 'zod'

const searchSchema = z.object({
  page: fallback(z.number(), 1).default(1),
  sort: fallback(z.enum(['newest', 'oldest']), 'newest').default('newest'),
})

export const Route = createFileRoute('/products')({
  validateSearch: zodValidator(searchSchema),
  component: () => {
    const { page, sort } = Route.useSearch()
    // Writing
    return <Link from={Route.fullPath} search={prev => ({ ...prev, page: prev.page + 1 })}>Next</Link>
  },
})

Use fallback(...).default(...) from the Zod adapter. Plain .catch() causes type loss.

Data Loading

export const Route = createFileRoute('/posts')({
  // loaderDeps: only extract what loader needs (not full search)
  loaderDeps: ({ search: { page } }) => ({ page }),
  loader: ({ deps: { page } }) => fetchPosts({ page }),
  pendingComponent: () => <Spinner />,
  component: () => {
    const posts = Route.useLoaderData()
    return <PostList posts={posts} />
  },
})

Route Context (Dependency Injection)

// __root.tsx
interface RouterContext { queryClient: QueryClient }
export const Route = createRootRouteWithContext<RouterContext>()({ component: Root })

// router.ts
const router = createRouter({ routeTree, context: { queryClient } })

// Child route - queryClient available in loader
export const Route = createFileRoute('/posts')({
  loader: ({ context: { queryClient } }) =>
    queryClient.ensureQueryData(postsQueryOptions()),
})

Router Deep Dives

  • router-guide.md - Complete Router reference with all patterns
  • search-params.md - Custom serialization, Standard Schema, sharing params
  • data-loading.md - Deferred loading, streaming SSR, shouldReload
  • routing-patterns.md - Virtual routes, route masking, navigation blocking
  • code-splitting.md - Automatic/manual splitting strategies
  • router-ssr.md - SSR setup, streaming, hydration

TanStack Start (RC)

Full-stack framework extending Router with SSR, server functions, middleware. API stable, feature-complete. No RSC yet.

Setup

pnpm create @tanstack/start@latest
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstac
Compartilhar X LinkedIn