Devtools patterns
Skill smicolon/ai-kit/packs/tanstack-router/skills/devtools-patterns
Convention packs for any AI coding tool - agents, skills, commands, and rules for 15 tools including Claude Code, Cursor, Windsurf, and Copilot
npx -y skills add smicolon/ai-kit --skill devtools-patternsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
TanStack DevTools patterns for debugging and development. Activates when setting up development tools, debugging queries, or configuring developer experience in React applications.
SKILL.md
8.8 KB, as published. Nobody here has run it
TanStack DevTools Patterns
This skill covers TanStack DevTools setup and usage for debugging React applications.
React Query DevTools
Installation
bun add -D @tanstack/react-query-devtools
Basic Setup
// main.tsx
import { QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { createQueryClient } from '@/lib/query-client'
const queryClient = createQueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
Production Lazy Loading
// Only load devtools in development
import { lazy, Suspense } from 'react'
const ReactQueryDevtools = lazy(() =>
import('@tanstack/react-query-devtools').then((d) => ({
default: d.ReactQueryDevtools,
}))
)
function App() {
return (
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
{import.meta.env.DEV && (
<Suspense fallback={null}>
<ReactQueryDevtools />
</Suspense>
)}
</QueryClientProvider>
)
}
DevTools Configuration
<ReactQueryDevtools
initialIsOpen={false}
position="bottom-right"
buttonPosition="bottom-right"
toggleButtonProps={{
style: {
marginBottom: '4rem', // Avoid overlapping with other UI
},
}}
/>
Embedded DevTools
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'
function AdminDashboard() {
const [isOpen, setIsOpen] = useState(false)
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>
Toggle Query Inspector
</button>
{isOpen && (
<div className="devtools-panel">
<ReactQueryDevtoolsPanel />
</div>
)}
</div>
)
}
Router DevTools
Installation
bun add -D @tanstack/router-devtools
Setup
// routes/__root.tsx
import { TanStackRouterDevtools } from '@tanstack/router-devtools'
export const Route = createRootRouteWithContext<RouterContext>()({
component: () => (
<>
<Outlet />
{import.meta.env.DEV && <TanStackRouterDevtools position="bottom-left" />}
</>
),
})
Lazy Loading Router DevTools
import { lazy, Suspense } from 'react'
const TanStackRouterDevtools = import.meta.env.DEV
? lazy(() =>
import('@tanstack/router-devtools').then((d) => ({
default: d.TanStackRouterDevtools,
}))
)
: () => null
export const Route = createRootRouteWithContext<RouterContext>()({
component: () => (
<>
<Outlet />
<Suspense fallback={null}>
<TanStackRouterDevtools />
</Suspense>
</>
),
})
Combined DevTools Layout
// routes/__root.tsx
import { lazy, Suspense } from 'react'
const ReactQueryDevtools = lazy(() =>
import('@tanstack/react-query-devtools').then((d) => ({
default: d.ReactQueryDevtools,
}))
)
const TanStackRouterDevtools = lazy(() =>
import('@tanstack/router-devtools').then((d) => ({
default: d.TanStackRouterDevtools,
}))
)
export const Route = createRootRouteWithContext<RouterContext>()({
component: () => (
<>
<Outlet />
{import.meta.env.DEV && (
<Suspense fallback={null}>
<ReactQueryDevtools position="bottom-right" />
<TanStackRouterDevtools position="bottom-left" />
</Suspense>
)}
</>
),
})
Query Logging
Development Logger
// lib/query-client.ts
import { QueryClient } from '@tanstack/react-query'
export function createQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
// ... other options
},
},
...(import.meta.env.DEV && {
logger: {
log: console.log,
warn: console.warn,
error: console.error,
},
}),
})
}
Custom Query Logger
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: (error, query) => {
if (import.meta.env.DEV) {
console.error(`Query Error [${query.queryKey}]:`, error)
}
},
onSuccess: (data, query) => {
if (import.meta.env.DEV) {
console.log(`Query Success [${query.queryKey}]:`, data)
}
},
}),
mutationCache: new MutationCache({
onError: (error, variables, context, mutation) => {
if (import.meta.env.DEV) {
console.error(`Mutation Error:`, error, variables)
}
},
}),
})
Performance Monitoring
Query Timing
const queryClient = new QueryClient({
queryCache: new QueryCache({
onSuccess: (data, query) => {
if (import.meta.env.DEV) {
const timing = query.state.dataUpdatedAt - query.state.fetchFailureCount
console.log(`[${query.queryKey}] fetched in ${timing}ms`)
}
},
}),
})
React Profiler Integration
import { Profiler } from 'react'
function App() {
const onRender = (
id: string,
phase: 'mount' | 'update',
actualDuration: number
) => {
if (import.meta.env.DEV && actualDuration > 16) {
console.warn(`Slow render: ${id} (${phase}) - ${actualDuration.toFixed(2)}ms`)
}
}
return (
<Profiler id="App" onRender={onRender}>
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
</Profiler>
)
}
Debug Utilities
Query State Inspector Hook
import { useQueryClient } from '@tanstack/react-query'
function useQueryInspector() {
const queryClient = useQueryClient()
return {
getQueryState: (queryKey: unknown[]) =>
queryClient.getQueryState(queryKey),
getQueriesData: () =>
queryClient.getQueriesData({ type: 'all' }),
invalidateAll: () =>
queryClient.invalidateQueries(),
clearAll: () =>
queryClient.clear(),
}
}
// Usage in development
function DevPanel() {
const inspector = useQueryInspector()
return (
<div>
<button onClick={() => console.log(inspector.getQueriesData())}>
Log All Queries
</button>
<button onClick={inspector.invalidateAll}>
Invalidate All
</button>
<button onClick={inspector.clearAll}>
Clear Cache
</button>
</div>
)
}
Route State Inspector
import { useRouter, useMatches } from '@tanstack/react-router'
function RouteInspector() {
const router = useRouter()
const matches = useMatches()
if (!import.meta.env.DEV) return null
return (
<div className="fixed bottom-20 left-4 bg-black/90 text-white p-4 rounded text-xs">
<pre>
{JSON.stringify(
{
pathname: router.state.location.pathname,
search: router.state.location.search,
matches: matches.map((m) => m.routeId),
},
null,
2
)}
</pre>
</div>
)
}
Vite Configuration
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
define: {
// Strip devtools from production
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
},
build: {
rollupOptions: {
// Exclude devtools from production bundle
external: import.meta.env.PROD
? ['@tanstack/react-query-devtools', '@tanstack/router-devtools']
: [],
},
},
})
Conventions
- Lazy load devtools - Always lazy load to reduce bundle size
- Environment check - Only render in development
- Position wisely - Avoid overlapping with app UI
- Production strip - Ensure devtools are excluded from prod
- Custom logging - Add meaningful logging in development
Anti-Patterns
// ❌ WRONG: DevTools in production bundle
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
function App() {
return (
<>
<Main />
<ReactQueryDevtools /> {/* Always included! */}
</>
)
}
// ✅ CORRECT: Conditional and lazy loaded
const ReactQueryDevtools = lazy(() =>
import('@tanstack/react-query-devtools').then((d) => ({
default: d.ReactQueryDevtools,
}))
)
function App() {
return (
<>
<Main />
{import.meta.env.DEV && (
<Suspense fallback={null}>
<ReactQueryDevtools />
</Suspense>
)}
</>
)
}
// ❌ WRONG: No position configuration (overlapping)
<ReactQueryDevtools />
<TanStackRouterDevtools />
// ✅ CORRECT: Different positions
<ReactQueryDevtools position="bottom-right" />
<TanStackRouterDevtools position="bottom-left" />