Files
dotfiles/dot_agents/skills/tanstack-router-best-practices/rules/preload-intent.md
T
Prad Nukala 6406208b35 shell: fish functions -> ~/.local/bin executables + bash/zsh ports; manage ~/.agents
- 16 cross-shell utilities converted to sh/bash chezmoi executables:
  ai c2p cp-appid extract_agents fab gdc gh-sonr-org-migrate ghg-clone
  ghn gho git-summary launch_omp_livediff_overlay lc-elir mdcp mdp pj
  (ghg-clone not named ghg: unrelated cargo binary owns that name)
- cwd/history helpers stay per-shell: yy c cd clip-last + ghg/ghn/gho
  cd-wrappers in bash/functions/ and zsh/functions/, loaded by rc files
- fish keeps only shell-state functions; converted ones now hit PATH
- cp-appid ignore gating moved to .local/bin path
- dot_agents/skills: 82 skills materialized from dotstate storage so
  dot_claude skill symlinks resolve on fresh bootstrap; dotstate
  profile/symlinks.json released .agents/skills .bashrc .zshrc
2026-08-10 15:15:06 -04:00

3.3 KiB

preload-intent: Enable Intent-Based Preloading

Priority: MEDIUM

Explanation

Configure defaultPreload: 'intent' to preload routes when users hover or focus links. This loads data before the click, making navigation feel instant.

Bad Example

// No preloading configured - data loads after click
const router = createRouter({
  routeTree,
  // No defaultPreload - user waits after every navigation
})

// Each navigation shows loading state
function PostList({ posts }: { posts: Post[] }) {
  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>
          <Link to="/posts/$postId" params={{ postId: post.id }}>
            {post.title}
          </Link>
          {/* Click → wait for data → render */}
        </li>
      ))}
    </ul>
  )
}

Good Example

// router.tsx - Enable preloading by default
const router = createRouter({
  routeTree,
  defaultPreload: 'intent',       // Preload on hover/focus
  defaultPreloadDelay: 50,        // Wait 50ms before starting
})

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

// Links automatically preload on hover
function PostList({ posts }: { posts: Post[] }) {
  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>
          <Link to="/posts/$postId" params={{ postId: post.id }}>
            {post.title}
          </Link>
          {/* Hover → preload starts → click → instant navigation */}
        </li>
      ))}
    </ul>
  )
}

Preload Options

// Router-level defaults
const router = createRouter({
  routeTree,
  defaultPreload: 'intent',       // 'intent' | 'render' | 'viewport' | false
  defaultPreloadDelay: 50,        // ms before preload starts
  defaultPreloadStaleTime: 30000, // 30s - how long preloaded data stays fresh
})

// Link-level overrides
<Link
  to="/heavy-page"
  preload={false}  // Disable for this specific link
>
  Heavy Page
</Link>

<Link
  to="/critical-page"
  preload="render"  // Preload immediately when Link renders
>
  Critical Page
</Link>

Preload Strategies

Strategy Behavior Use Case
'intent' Preload on hover/focus Default for most links
'render' Preload when Link mounts Critical next pages
'viewport' Preload when Link enters viewport Below-fold content
false No preloading Heavy, rarely-visited pages

Good Example: With TanStack Query Integration

// When using TanStack Query, disable router cache
const router = createRouter({
  routeTree,
  defaultPreload: 'intent',
  defaultPreloadStaleTime: 0,  // Let TanStack Query manage cache
  context: {
    queryClient,
  },
})

// Route loader uses TanStack Query
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params, context: { queryClient } }) => {
    // ensureQueryData respects TanStack Query's staleTime
    await queryClient.ensureQueryData(postQueries.detail(params.postId))
  },
})

Context

  • Preloading loads route code AND executes loaders
  • preloadDelay prevents excessive requests on quick mouse movements
  • Preloaded data is garbage collected after preloadStaleTime
  • Works with both router caching and external caching (TanStack Query)
  • Mobile: Consider 'viewport' since hover isn't available
  • Monitor network tab to verify preloading works correctly