본문으로 건너뛰기
cccskills
무료GitHub에 공개

router-core-navigation

Link component, useNavigate, Navigate component, router.navigate, ToOptions/NavigateOptions/LinkOptions, from/to relative navigation, activeOptions/activeProps, preloading (intent/viewport/render), preloadDelay, navigation blocking (useBlocker, Block), createLink, linkOptions helper, scroll restoration, MatchRoute.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md10.5 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Navigation

Setup

Basic type-safe Link with to and params:

import { Link } from '@tanstack/react-router'

function PostLink({ postId }: { postId: string }) {
  return (
    <Link to="/posts/$postId" params={{ postId }}>
      View Post
    </Link>
  )
}

Core Patterns

Link with Active States

import { Link } from '@tanstack/react-router'

function NavLink() {
  return (
    <Link
      to="/posts"
      activeProps={{ className: 'font-bold' }}
      inactiveProps={{ className: 'text-gray-500' }}
      activeOptions={{ exact: true }}
    >
      Posts
    </Link>
  )
}

The data-status attribute is also set to "active" on active links for CSS-based styling.

activeOptions controls matching behavior:

  • exact (default false) — when true, only matches the exact path (not children)
  • includeHash (default false) — include hash in active matching
  • includeSearch (default true) — include search params in active matching

Children can receive isActive as a render function:

<Link to="/posts">
  {({ isActive }) => <span className={isActive ? 'font-bold' : ''}>Posts</span>}
</Link>

Relative Navigation with from

Without from, navigation resolves from root /. To use relative paths like .., provide from:

import { createFileRoute, Link } from '@tanstack/react-router'

export const Route = createFileRoute('/posts/$postId')({
  component: PostComponent,
})

function PostComponent() {
  return (
    <div>
      {/* Relative to current route */}
      <Link from={Route.fullPath} to="..">
        Back to Posts
      </Link>

      {/* "." reloads the current route */}
      <Link from={Route.fullPath} to=".">
        Reload
      </Link>
    </div>
  )
}

useNavigate for Programmatic Navigation

Use useNavigate only for side-effect-driven navigation (e.g., after a form submission). For anything the user clicks, prefer Link.

import { useNavigate } from '@tanstack/react-router'

function CreatePostForm() {
  const navigate = useNavigate({ from: '/posts' })

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    const response = await fetch('/api/posts', { method: 'POST', body: '...' })
    const { id: postId } = await response.json()

    if (response.ok) {
      navigate({ to: '/posts/$postId', params: { postId } })
    }
  }

  return <form onSubmit={handleSubmit}>{/* ... */}</form>
}

The Navigate component performs an immediate client-side navigation on mount:

import { Navigate } from '@tanstack/react-router'

function LegacyRedirect() {
  return <Navigate to="/posts/$postId" params={{ postId: 'my-first-post' }} />
}

router.navigate is available anywhere you have the router instance, including outside of React.

Preloading

Strategies: intent (hover/touchstart), viewport (intersection observer), render (on mount).

Set globally:

import { createRouter } from '@tanstack/react-router'

const router = createRouter({
  routeTree,
  defaultPreload: 'intent',
  defaultPreloadDelay: 50, // ms, default is 50
})

Or per-link:

<Link
  to="/posts/$postId"
  params={{ postId }}
  preload="intent"
  preloadDelay={100}
>
  View Post
</Link>

Preloaded data stays fresh for 30 seconds by default (defaultPreloadStaleTime: 30_000). During that window it won't be refetched. When using an external cache like TanStack Query, set defaultPreloadStaleTime: 0 to let the external library control freshness.

Manual preloading via the router instance:

import { useRouter } from '@tanstack/react-router'

function Component() {
  const router = useRouter()

  useEffect(() => {
    router.preloadRoute({ to: '/posts/$postId', params: { postId: '1' } })
  }, [router])

  return <div />
}

Navigation Blocking

Use useBlocker to prevent navigation when a form has unsaved changes:

import { useBlocker } from '@tanstack/react-router'
import { useState } from 'react'

function EditForm() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  useBlocker({
    shouldBlockFn: () => {
      if (!formIsDirty) return false
      const shouldLeave = confirm('Are you sure you want to leave?')
      return !shouldLeave
    },
  })

  return <form>{/* ... */}</form>
}

With custom UI using withResolver:

import { useBlocker } from '@tanstack/react-router'
import { useState } from 'react'

function EditForm() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  const { proceed, reset, status } = useBlocker({
    shouldBlockFn: () => formIsDirty,
    withResolver: true,
  })

  return (
    <>
      <form>{/* ... */}</form>
      {status === 'blocked' && (
        <div>
          <p>Are you sure you want to leave?</p>
          <button onClick={proceed}>Yes</button>
          <button onClick={reset}>No</button>
        </div>
      )}
    </>
  )
}

Control beforeunload separately:

useBlocker({
  shouldBlockFn: () => formIsDirty,
  enableBeforeUnload: formIsDirty,
})

linkOptions for Reusable Navigation Options

linkOptions provides eager type-checking on navigation options objects, so errors surface at definition, not at spread-site:

import {
  linkOptions,
  Link,
  useNavigate,
  redirect,
} from '@tanstack/react-router'

const dashboardLinkOptions = linkOptions({
  to: '/dashboard',
  search: { search: '' },
})

// Use anywhere: Link, navigate, redirect
function Nav() {
  const navigate = useNavigate()

  return (
    <div>
      <Link {...dashboardLinkOptions}>Dashboard</Link>
      <button onClick={() => navigate(dashboardLinkOptions)}>Go</button>
    </div>
  )
}

// Also works in an array for navigation bars
const navOptions = linkOptions([
  { to: '/dashboard', label: 'Summary', activeOptions: { exact: true } },
  { to: '/dashboard/invoices', label: 'Invoices' },
  { to: '/dashboard/users', label: 'Users' },
])

function NavBar() {
  return (
    <nav>
      {navOptions.map((option) => (
        <Link
          {...option}
          key={option.to}
          activeProps={{ className: 'font-bold' }}
        >
          {option.label}
        </Link>
      ))}
    </nav>
  )
}

createLink for Custom Components

Wraps any component with TanStack Router's type-safe navigation:

import * as React from 'react'
import { createLink, LinkComponent } from '@tanstack/react-router'

interface BasicLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {}

const BasicLinkComponent = React.forwardRef<HTMLAnchorElement, BasicLinkProps>(
  (props, ref) => {
    return <a ref={ref} {...props} className="block px-3 py-2 text-blue-700" />
  },
)

const CreatedLinkComponent = createLink(BasicLinkComponent)

export const CustomLink: LinkComponent<typeof BasicLinkComponent> = (props) => {
  return <CreatedLinkComponent preload="intent" {...props} />
}

Usage retains full type safety:

<CustomLink to="/dashboard/invoices/$invoiceId" params={{ invoiceId: 0 }} />

Scroll Restoration

Enable globally on the router:

const router = createRouter({
  routeTree,
  scrollRestoration: true,
})

For nested scrollable areas:

const router = createRouter({
  routeTree,
  scrollRestoration: true,
  scrollToTopSelectors: ['#main-scrollable-area'],
})

Custom cache keys:

const router = createRouter({
  routeTree,
  scrollRestoration: true,
  getScrollRestorationKey: (location) => location.pathname,
})

Prevent scroll reset for a specific navigation:

<Link to="/posts" resetScroll={false}>
  Posts
</Link>

MatchRoute for Pending UI

import { Link, MatchRoute } from '@tanstack/react-router'

function Nav() {
  return (
    <Link to="/users">
      Users
      <MatchRoute to="/users" pending>
        <Spinner />
      </MatchRoute>
    </Link>
  )
}

Common Mistakes

CRITICAL: Interpolating params into the to string

// WRONG — breaks type safety and param encoding
<Link to={`/posts/${postId}`}>Post</Link>

// CORRECT — use the params option
<Link to="/posts/$postId" params={{ postId }}>Post</Link>

Dynamic segments are declared with $ in the route path. Always pass them via params. This applies to Link, useNavigate, Navigate, and router.navigate.

MEDIUM: Using useNavigate for clickable elements

// WRONG — no href, no cmd+click, no preloading, no accessibility
function BadNav() {
  const navigate = useNavigate()
  return <button onClick={() => navigate({ to: '/posts' })}>Posts</button>
}

// CORRECT — real <a> tag with href, accessible, preloadable
function GoodNav() {
  return <Link to="/posts">Posts</Link>
}

Use useNavigate only for programmatic side-effect navigation (after form submit, async action, etc).

HIGH: Not providing from for relative navigation

// WRONG — without from, ".." resolves from root
<Link to="..">Back</Link>

// CORRECT — provide from for relative resolution
<Link from={Route.fullPath} to="..">Back</Link>

Without from, only absolute paths are autocompleted and type-safe. Relative paths like .. resolve from root instead of the current route.

HIGH: Using search as object instead of function loses existing params

// WRONG — replaces ALL search params with just { page: 2 }
<Link to="." search={{ page: 2 }}>Page 2</Link>

// CORRECT — preserves existing search params, updates page
<Link to="." search={(prev) => ({ ...prev, page: 2 })}>Page 2</Link>

When you pass search as a plain object, it replaces all search params. Use the function form to spread previous params and selectively update.


Cross-References

  • See also: router-core-search-params/SKILL.md — Link search prop interacts with search param validation
  • See also: router-core-type-safety/SKILL.md — from narrowing improves type inference on Link

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Build AI chat interfaces using ai-elements components — conversations, messages, tool displays, prompt inputs, and more. Use when the user wants to build a chatbot, AI assistant UI, or any AI-powered chat interface.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

hashintel/brunch82026년 10월 17일 업데이트

Search the live web via Perplexity Search API. Use when you need current documentation, release notes, vendor pages, news, domain-constrained web search, or date/recency filtering. Not for local codebase search or stable docs already in context.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

hashintel/brunch82026년 10월 17일 업데이트

cli-cdp

무료

Chrome DevTools CLI for browser automation via shell commands. Use when interacting with web pages from the command line — navigating, clicking, filling forms, inspecting console/network, taking screenshots, or extracting page content. Triggers on: browse a page, automate Chrome, inspect console, check network requests, take a screenshot, fill a form, click a button.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

hashintel/brunch82026년 10월 17일 업데이트

Uses the chrome-devtools-axi CLI for browser automation, accessibility-tree snapshots, console and network inspection, screenshots, Lighthouse audits, and performance traces. Use when interacting with Chrome from the shell, especially when the user mentions chrome-devtools-axi, AX snapshots, browser debugging, or DevTools automation from the command line.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

hashintel/brunch82026년 10월 17일 업데이트

cli-cmux

무료

Deep expertise in cmux — the terminal multiplexer with native browser views. Use when managing panes, reading terminal output, sending keystrokes, opening browser views, or manually testing web UIs and TUIs inside cmux. Triggers on: cmux, open a browser pane, split terminal, read screen, send keys, test this UI in cmux, preview in cmux.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

hashintel/brunch82026년 10월 17일 업데이트

Uses the gh-axi CLI for GitHub shell operations: issue, pull request, workflow run, release, repo, search, and API tasks. Prefer this over regular `gh` for GitHub reads and simple mutations when an agent needs compact, structured, suggestion-rich output. Triggers on: gh, GitHub CLI, github issue, github pr, pull request, workflow run, github release, gh api, repo inspection, list PRs, view issue, check workflow runs, inspect repo, GitHub shell operations.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

hashintel/brunch82026년 10월 17일 업데이트

hashintel의 스킬 모두 보기

이 스킬의 문제 신고