agentsclimarketplace

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

Install
npx -y skills add smicolon/ai-kit --skill devtools-patterns

Assembled 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

  1. Lazy load devtools - Always lazy load to reduce bundle size
  2. Environment check - Only render in development
  3. Position wisely - Avoid overlapping with app UI
  4. Production strip - Ensure devtools are excluded from prod
  5. 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" />

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.