diff --git a/README.md b/README.md
index 63c6a2ff..f613659a 100644
--- a/README.md
+++ b/README.md
@@ -181,6 +181,7 @@ By creating a `.cursorrules` file in your project's root directory, you can leve
- [React (Redux, TypeScript)](./rules/react-redux-typescript-cursorrules-prompt-file/.cursorrules) - Cursor rules for React development with Redux and TypeScript integration.
- [React (MobX)](./rules/react-mobx-cursorrules-prompt-file/.cursorrules) - Cursor rules for React development with MobX integration.
- [React (React Query)](./rules/react-query-cursorrules-prompt-file/.cursorrules) - Cursor rules for React development with React Query integration.
+- [React (TanStack Router + Query)](./rules/react-tanstack-router-query-cursorrules-prompt-file/.cursorrules) - Cursor rules for React SPAs combining TanStack Router v1 and TanStack Query v5 for zero-loading-spinner routing and type-safe server state.
### Database and API
diff --git a/rules-new/react-tanstack-router-query.mdc b/rules-new/react-tanstack-router-query.mdc
new file mode 100644
index 00000000..02eff204
--- /dev/null
+++ b/rules-new/react-tanstack-router-query.mdc
@@ -0,0 +1,117 @@
+---
+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
+
+
+
+```
+
+## 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
{post!.title}
+}
+```
+
+## 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
+ queryClient.prefetchQuery(postQueryOptions(post.id))}
+>
+ {post.title}
+
+```
+
+## 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
diff --git a/rules/react-tanstack-router-query-cursorrules-prompt-file/.cursorrules b/rules/react-tanstack-router-query-cursorrules-prompt-file/.cursorrules
new file mode 100644
index 00000000..bc38eb23
--- /dev/null
+++ b/rules/react-tanstack-router-query-cursorrules-prompt-file/.cursorrules
@@ -0,0 +1,268 @@
+You are an expert in React, TanStack Router v1, TanStack Query v5, TypeScript, Vite, and building fully type-safe single-page applications.
+
+# React + TanStack Router + TanStack Query Guidelines
+
+## Architecture Overview
+- TanStack Router handles all routing, URL state, and navigation
+- TanStack Query manages all server state, caching, and async data
+- React components are pure UI — they read from Query cache and trigger mutations
+- Loaders bridge Router and Query: they prefetch into the Query cache before render
+- This eliminates loading spinners for route-level data; Suspense handles component-level loading
+
+## Project Setup
+```
+src/
+ routes/
+ __root.tsx
+ index.tsx
+ posts/
+ index.tsx
+ $postId.tsx
+ queries/ ← Query definitions (queryOptions factories)
+ posts.ts
+ users.ts
+ api/ ← API client functions (fetchers)
+ posts.ts
+ users.ts
+ lib/
+ queryClient.ts
+ router.ts
+ main.tsx
+```
+
+## QueryClient + Router Setup
+```ts
+// src/lib/queryClient.ts
+import { QueryClient } from '@tanstack/react-query'
+
+export const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 1000 * 60,
+ retry: (count, error: any) => error?.status !== 404 && count < 2,
+ },
+ },
+})
+```
+
+```tsx
+// src/lib/router.ts
+import { createRouter } from '@tanstack/react-router'
+import { routeTree } from '../routeTree.gen'
+import { queryClient } from './queryClient'
+
+export const router = createRouter({
+ routeTree,
+ context: { queryClient },
+ defaultPreload: 'intent',
+ defaultPreloadStaleTime: 0,
+})
+
+declare module '@tanstack/react-router' {
+ interface Register {
+ router: typeof router
+ }
+}
+```
+
+```tsx
+// src/main.tsx
+import { RouterProvider } from '@tanstack/react-router'
+import { QueryClientProvider } from '@tanstack/react-query'
+import { router } from './lib/router'
+import { queryClient } from './lib/queryClient'
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+)
+```
+
+## Query Definitions (queryOptions factories)
+- Co-locate query key, fetcher, and staleTime in one place
+- Share between Router loaders and component hooks
+```ts
+// src/queries/posts.ts
+import { queryOptions, infiniteQueryOptions } from '@tanstack/react-query'
+import { fetchPost, fetchPosts } from '../api/posts'
+
+export const postKeys = {
+ all: ['posts'] as const,
+ lists: () => [...postKeys.all, 'list'] as const,
+ list: (filters?: PostFilters) => [...postKeys.lists(), filters] as const,
+ details: () => [...postKeys.all, 'detail'] as const,
+ detail: (id: string) => [...postKeys.details(), id] as const,
+}
+
+export const postDetailQueryOptions = (id: string) =>
+ queryOptions({
+ queryKey: postKeys.detail(id),
+ queryFn: () => fetchPost(id),
+ staleTime: 1000 * 60 * 5,
+ })
+
+export const postsListQueryOptions = (filters?: PostFilters) =>
+ queryOptions({
+ queryKey: postKeys.list(filters),
+ queryFn: () => fetchPosts(filters),
+ staleTime: 1000 * 60,
+ })
+```
+
+## Router Loader + Query Integration
+- Loaders call `queryClient.ensureQueryData` — populates cache, renders immediately without spinner
+- Components then call `useQuery` with the same options — reads from cache synchronously
+```tsx
+// src/routes/posts/$postId.tsx
+import { createFileRoute } from '@tanstack/react-router'
+import { useQuery } from '@tanstack/react-query'
+import { postDetailQueryOptions } from '../../queries/posts'
+
+export const Route = createFileRoute('/posts/$postId')({
+ loader: ({ context: { queryClient }, params }) =>
+ queryClient.ensureQueryData(postDetailQueryOptions(params.postId)),
+
+ errorComponent: ({ error }) => ,
+ pendingComponent: PostSkeleton,
+ component: PostDetail,
+})
+
+function PostDetail() {
+ const { postId } = Route.useParams()
+ // data is already in cache from loader — no loading state
+ const { data: post } = useQuery(postDetailQueryOptions(postId))
+
+ return {post!.title}
+}
+```
+
+## Search Params + Query Integration
+- Use TanStack Router search params as the source of truth for filter/pagination state
+- Pass search params into queryOptions to drive query key and fetcher
+```tsx
+// src/routes/posts/index.tsx
+import { createFileRoute, Link } from '@tanstack/react-router'
+import { useQuery } from '@tanstack/react-query'
+import { z } from 'zod'
+import { postsListQueryOptions } from '../../queries/posts'
+
+const searchSchema = z.object({
+ page: z.number().int().min(1).default(1),
+ category: z.string().optional(),
+})
+
+export const Route = createFileRoute('/posts/')({
+ validateSearch: searchSchema,
+ loader: ({ context: { queryClient }, location: { search } }) =>
+ queryClient.ensureQueryData(postsListQueryOptions(search)),
+ component: PostsList,
+})
+
+function PostsList() {
+ const search = Route.useSearch()
+ const navigate = Route.useNavigate()
+ const { data: posts } = useQuery(postsListQueryOptions(search))
+
+ return (
+
+ {posts?.map(post => (
+
+ {post.title}
+
+ ))}
+
+
+ )
+}
+```
+
+## Mutations
+```tsx
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import { useNavigate } from '@tanstack/react-router'
+import { postKeys } from '../../queries/posts'
+
+function CreatePostForm() {
+ const queryClient = useQueryClient()
+ const navigate = useNavigate()
+
+ const mutation = useMutation({
+ mutationFn: createPost,
+ onSuccess: (newPost) => {
+ // Populate detail cache immediately
+ queryClient.setQueryData(postKeys.detail(newPost.id), newPost)
+ // Invalidate list queries
+ queryClient.invalidateQueries({ queryKey: postKeys.lists() })
+ // Navigate to new post (no loading — cache is warm)
+ navigate({ to: '/posts/$postId', params: { postId: newPost.id } })
+ },
+ })
+
+ return (/* form JSX */)
+}
+```
+
+## Authentication Pattern
+```tsx
+// src/routes/__root.tsx
+import { createRootRouteWithContext } from '@tanstack/react-router'
+
+export interface RouterContext {
+ queryClient: QueryClient
+ auth: { isAuthenticated: boolean; user: User | null }
+}
+
+export const Route = createRootRouteWithContext()({
+ component: RootLayout,
+})
+
+// src/routes/_auth.tsx (pathless layout for protected routes)
+export const Route = createFileRoute('/_auth')({
+ beforeLoad: ({ context }) => {
+ if (!context.auth.isAuthenticated) {
+ throw redirect({ to: '/login', search: { redirect: location.pathname } })
+ }
+ },
+})
+```
+
+## Prefetching on Hover
+```tsx
+function PostCard({ post }: { post: Post }) {
+ const queryClient = useQueryClient()
+ return (
+ queryClient.prefetchQuery(postDetailQueryOptions(post.id))}
+ >
+ {post.title}
+
+ )
+}
+```
+
+## DevTools (Development Only)
+```tsx
+// In __root.tsx
+import { TanStackRouterDevtools } from '@tanstack/router-devtools'
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
+
+// Inside component
+{import.meta.env.DEV && (
+ <>
+
+
+ >
+)}
+```
+
+## Key Rules
+- Always define `queryOptions` outside of components — not inline in `useQuery()`
+- Never use `useEffect` to fetch data — use loaders or `useQuery`
+- Always type router context — `declare module '@tanstack/react-router'` registration is required
+- Search params are the only source of truth for URL-driven filter state
+- Mutations should `setQueryData` + `invalidateQueries`, not just invalidate, for instant UI feedback
diff --git a/rules/react-tanstack-router-query-cursorrules-prompt-file/README.md b/rules/react-tanstack-router-query-cursorrules-prompt-file/README.md
new file mode 100644
index 00000000..1b0091d1
--- /dev/null
+++ b/rules/react-tanstack-router-query-cursorrules-prompt-file/README.md
@@ -0,0 +1,18 @@
+# React + TanStack Router + TanStack Query Cursor Rules
+
+Cursor rules for building React SPAs with TanStack Router v1 and TanStack Query v5 working together — the definitive pattern for type-safe routing, data loading, and server state management without loading spinners.
+
+## What's covered
+- QueryClient + Router setup with shared context
+- `queryOptions` factories co-located with query keys
+- Router loader → Query cache integration (zero loading states)
+- Search params as filter/pagination state driver
+- Mutation pattern with cache population + invalidation
+- Auth guards using router context + `beforeLoad`
+- Hover prefetching
+- DevTools setup for both Router and Query
+
+## Author
+Created by [usm4nhafeez](https://github.com/usm4nhafeez)
+
+Contributed to [awesome-cursorrules](https://github.com/PatrickJS/awesome-cursorrules)
diff --git a/rules/react-tanstack-router-query-cursorrules-prompt-file/react-tanstack-router-query.mdc b/rules/react-tanstack-router-query-cursorrules-prompt-file/react-tanstack-router-query.mdc
new file mode 100644
index 00000000..02eff204
--- /dev/null
+++ b/rules/react-tanstack-router-query-cursorrules-prompt-file/react-tanstack-router-query.mdc
@@ -0,0 +1,117 @@
+---
+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
+
+
+
+```
+
+## 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 {post!.title}
+}
+```
+
+## 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
+ queryClient.prefetchQuery(postQueryOptions(post.id))}
+>
+ {post.title}
+
+```
+
+## 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