Skip to main content
rule

react-tanstack-router-query — Cursor rule: what it does and setup

Guides AI assistants on structuring React SPAs with TanStack Router v1 and TanStack Query v5 to fetch data in loaders before rendering.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/react-tanstack-router-query.mdc
Open on GitHub

What react-tanstack-router-query does

The rule guides Cursor to generate React SPA architecture using Vite and TypeScript. Route loaders call queryClient.ensureQueryData to populate server state before rendering, which prevents route data spinners. Routing and URL state remain strictly within TanStack Router, while TanStack Query handles caching and mutations.

Code conventions enforce declaring queryOptions factories outside components and rule out useEffect for data fetching. Search parameters act as the single source of truth for filters and pagination, validated through Zod schemas. Successful mutations update the cache immediately via setQueryData before navigating to the target screen.

The rule activates for src/routes/**/*, src/queries/**/*, src/lib/router.ts, and src/lib/queryClient.ts. Registering the router via declare module '@tanstack/react-router' is mandatory, because TypeScript cannot check route paths and parameters without it.

What it helps with

  • Integrate TanStack Router v1 with TanStack Query v5 without route spinners
  • Move data fetching logic out of components into route loaders
  • Tie URL search parameters directly to Query cache keys
  • Warm the query cache on mutation success before triggering navigation

Example prompts for the agent

  • Create route src/routes/posts/$postId.tsx with a loader using postQueryOptions and render the post.
  • Implement a paginated posts list using URL search parameters validated with Zod.
  • Add a post creation mutation that warms the cache with setQueryData and navigates to the new item.

How to install react-tanstack-router-query

Manually: put the files in place

.cursor/rules/react-tanstack-router-query.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about react-tanstack-router-query

How to install react-tanstack-router-query in Cursor?

Place the rule file at .cursor/rules/react-tanstack-router-query.mdc in your project repository.

What files trigger this rule?

Cursor evaluates it when working in src/routes/**/*, src/queries/**/*, src/lib/router.ts, or src/lib/queryClient.ts.

Why does this pattern avoid useEffect for data fetching?

Route loaders prefetch data with queryClient.ensureQueryData before UI mounts, so components read ready data straight from cache.

Source text

react-tanstack-router-query.mdc · CC0-1.0
---
description: "React SPA with TanStack Router v1 + TanStack Query v5 — the definitive pattern for zero-loading-spinner routing, type-safe URLs, and cache-first data"
globs: ["src/routes/**/*", "src/queries/**/*", "src/lib/router.ts", "src/lib/queryClient.ts"]
alwaysApply: false
---
You are an expert in React, TanStack Router v1, TanStack Query v5, TypeScript, and Vite.

## Architecture
- TanStack Router: routing, URL state, navigation
- TanStack Query: server state, caching, mutations
- Loader = bridge: prefetches into Query cache before render → zero loading spinners for route data
- Components are pure UI: read from Query cache, trigger mutations

## Setup
```ts
// src/lib/queryClient.ts
export const queryClient = new QueryClient({
  defaultOptions: { queries: { staleTime: 60_000 } },
})

// src/lib/router.ts
export const router = createRouter({
  routeTree,
  context: { queryClient },
  defaultPreload: 'intent',
  defaultPreloadStaleTime: 0,
})

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

// src/main.tsx
<QueryClientProvider client={queryClient}>
  <RouterProvider router={router} context={{ queryClient }} />
</QueryClientProvider>
```

## Query Definitions
```ts
// src/queries/posts.ts
export const postKeys = {
  all: ['posts'] as const,
  detail: (id: string) => [...postKeys.all, 'detail', id] as const,
  list: (f?: PostFilters) => [...postKeys.all, 'list', f] as const,
}

export const postQueryOptions = (id: string) =>
  queryOptions({ queryKey: postKeys.detail(id), queryFn: () => fetchPost(id) })

export const postsQueryOptions = (filters?: PostFilters) =>
  queryOptions({ queryKey: postKeys.list(filters), queryFn: () => fetchPosts(filters) })
```

## Loader + Component (zero loading state)
```tsx
export const Route = createFileRoute('/posts/$postId')({
  loader: ({ context: { queryClient }, params }) =>
    queryClient.ensureQueryData(postQueryOptions(params.postId)),
  component: PostDetail,
})

function PostDetail() {
  const { postId } = Route.useParams()
  const { data: post } = useQuery(postQueryOptions(postId))  // always in cache from loader
  return <h1>{post!.title}</h1>
}
```

## Search Params → Query Key
```tsx
const searchSchema = z.object({ page: z.number().default(1), q: z.string().optional() })

export const Route = createFileRoute('/posts/')({
  validateSearch: searchSchema,
  loader: ({ context: { queryClient }, location: { search } }) =>
    queryClient.ensureQueryData(postsQueryOptions(search)),
  component: PostsList,
})

function PostsList() {
  const search = Route.useSearch()
  const { data } = useQuery(postsQueryOptions(search))
  // ...
}
```

## Mutations
```tsx
const mutation = useMutation({
  mutationFn: createPost,
  onSuccess: (newPost) => {
    queryClient.setQueryData(postKeys.detail(newPost.id), newPost)  // warm cache
    queryClient.invalidateQueries({ queryKey: postKeys.list() })
    navigate({ to: '/posts/$postId', params: { postId: newPost.id } })  // instant — no spinner
  },
})
```

## Hover Prefetching
```tsx
<Link
  to="/posts/$postId"
  params={{ postId: post.id }}
  onMouseEnter={() => queryClient.prefetchQuery(postQueryOptions(post.id))}
>
  {post.title}
</Link>
```

## Key Rules
- Always define `queryOptions` outside components — never inline inside `useQuery()`
- Never use `useEffect` for data fetching — use loaders or `useQuery`
- Search params are the single source of truth for filter/pagination state
- After mutations: `setQueryData` + `invalidateQueries` for instant UI feedback
- `declare module '@tanstack/react-router'` router registration is required for full type safety

© PatrickJS · CC0-1.0 · PatrickJS/awesome-cursorrules

Similar IDE rules

All IDE rules for Frontend and UI →

From the same set210

react This rule hands Cursor a set of React conventions: component structure, hooks, state, forms and accessibility. Useful when a project has plenty of .tsx and .jsx and you want one style. ★ 41,024repo stars react-zustand-cursorrules-prompt-file Cursor guidance for React and TypeScript projects using Zustand. It standardizes store design, stops unnecessary re-renders, and keeps server state separate from client stores. ★ 41,024repo stars react-typescript-symfony-cursorrules-prompt-file A Cursor rule that steers the assistant toward readable code across React, TypeScript, PHP, Symfony and Docker. ★ 41,024repo stars react-typescript-nextjs-nodejs-cursorrules-prompt- A Cursor rule that pins down React, TypeScript, Next.js 14 and Node.js conventions: functional components, RORO, minimal use client. ★ 41,024repo stars react-tanstack-router-query-cursorrules-prompt-file This rule describes a React SPA setup where TanStack Router handles navigation and TanStack Query stores server data. Use it to fetch route data before rendering a page. ★ 41,024repo stars react-styled-components-cursorrules-prompt-file A Cursor rule that tells the agent how to write React code with styled-components: functional components, theming through ThemeProvider, a fixed folder layout. ★ 41,024repo stars react-router-v7 Enforces React Router v7 conventions for route modules, deterministic loaders, action mutations, and typed routing in Cursor. ★ 41,024repo stars react-redux-typescript-cursorrules-prompt-file This Cursor rule guides React work with Redux and TypeScript, covering component style, state structure, and typed hooks. Use it in a matching project. ★ 41,024repo stars

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor