[{"data":1,"prerenderedAt":130},["ShallowReactive",2],{"qa-\u002Freact\u002Frouting\u002Fnavigation-hooks":3},{"page":4,"siblings":114,"blog":127},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":105,"related":106,"seo":107,"seoDescription":108,"stem":109,"subtopic":6,"topic":110,"topicSlug":111,"updated":112,"__hash__":113},"qa\u002Freact\u002Frouting\u002Fnavigation-hooks.md","Navigation Hooks",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","React","react",{},true,3,"\u002Freact\u002Frouting\u002Fnavigation-hooks",[23,28,32,36,40,44,48,52,56,60,64,69,73,77,81,85,89,93,97,101],{"id":24,"difficulty":25,"q":26,"a":27},"use-navigate-basic","easy","What does useNavigate return and how do you use it for programmatic navigation?","**useNavigate** returns a **navigate function** that lets you redirect the user programmatically — typically inside event handlers, effects, or after async operations. Calling `navigate('\u002Fpath')` performs a **push** (adds a history entry); passing `{ replace: true }` performs a **replace** (overwrites the current entry).\n\n```jsx\nimport { useNavigate } from 'react-router-dom';\n\nfunction LoginForm() {\n  const navigate = useNavigate();\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    await login(credentials);\n    \u002F\u002F Push a new entry so the user can press Back to return to login\n    navigate('\u002Fdashboard');\n    \u002F\u002F Or replace so pressing Back does NOT return to this form:\n    \u002F\u002F navigate('\u002Fdashboard', { replace: true });\n  }\n\n  return \u003Cform onSubmit={handleSubmit}>...\u003C\u002Fform>;\n}\n```\n\n**Rule of thumb:** Use `replace: true` after authentication or any flow where letting the user navigate back to the previous page would be confusing or insecure.\n",{"id":29,"difficulty":25,"q":30,"a":31},"navigate-replace-vs-push","What is the difference between navigate('\u002Fpath') and navigate('\u002Fpath', { replace: true })?","Without options, **navigate('\u002Fpath')** is a **push**: it appends a new entry to the history stack, so pressing the browser Back button returns to the previous URL. With `{ replace: true }` it is a **replace**: the current history entry is overwritten and the previous URL is no longer reachable via Back.\n\n```jsx\nfunction CheckoutSuccess() {\n  const navigate = useNavigate();\n\n  useEffect(() => {\n    \u002F\u002F Replace so users can't press Back and accidentally re-submit the order\n    navigate('\u002Forder-confirmation', { replace: true });\n  }, [navigate]);\n\n  return null;\n}\n```\n\n**Rule of thumb:** Prefer `replace` for redirects that follow a destructive or one-way action (form submit, logout, payment confirmation); prefer the default push for normal link-like navigation.\n",{"id":33,"difficulty":25,"q":34,"a":35},"navigate-back","How do you navigate back one step in history with useNavigate?","Pass the **integer `-1`** to `navigate` to go back one entry, `-2` for two entries, or `+1` to go forward. This mirrors the `window.history.go(delta)` API.\n\n```jsx\nfunction BackButton() {\n  const navigate = useNavigate();\n\n  return (\n    \u002F\u002F navigate(-1) pops the history stack by one entry\n    \u003Cbutton onClick={() => navigate(-1)}>← Back\u003C\u002Fbutton>\n  );\n}\n```\n\n**Rule of thumb:** Use `navigate(-1)` instead of `window.history.back()` so React Router stays in control of the transition; `window.history.back()` bypasses React Router's transition listeners and blockers.\n",{"id":37,"difficulty":14,"q":38,"a":39},"navigate-state-passing","How do you pass state when navigating with useNavigate and how do you read it on the destination page?","Pass a **state** key in the options object. The state is stored in the history entry and is accessible on the destination via `useLocation().state`. It is **not** reflected in the URL so it disappears on a hard refresh.\n\n```jsx\n\u002F\u002F Source page\nfunction ProductCard({ product }) {\n  const navigate = useNavigate();\n  return (\n    \u003Cbutton onClick={() => navigate('\u002Fcheckout', { state: { productId: product.id } })}>\n      Buy now\n    \u003C\u002Fbutton>\n  );\n}\n\n\u002F\u002F Destination page\nimport { useLocation } from 'react-router-dom';\n\nfunction CheckoutPage() {\n  const { state } = useLocation();\n  \u002F\u002F state.productId is available here; undefined after a hard refresh\n  return \u003Cp>Checking out product {state?.productId}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Use location state for transient UI hints (e.g., \"came from search results\") — never for critical data that must survive a refresh; persist that in a store or URL param instead.\n",{"id":41,"difficulty":25,"q":42,"a":43},"use-params-basic","What does useParams return and when would you use it?","**useParams** returns an **object whose keys match the dynamic segments** declared in the route path (prefixed with `:`). Use it whenever a component needs to read a URL parameter like an ID or slug.\n\n```jsx\n\u002F\u002F Route definition\n\u002F\u002F \u003CRoute path=\"\u002Fusers\u002F:userId\u002Fposts\u002F:postId\" element={\u003CPostDetail \u002F>} \u002F>\n\nfunction PostDetail() {\n  const { userId, postId } = useParams();\n  \u002F\u002F Both values are strings — always parse numbers before comparing\n  const numericPostId = Number(postId);\n\n  return \u003Cp>User {userId}, Post {numericPostId}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Always treat `useParams` values as **strings** and validate\u002Fparse them before use — a missing or malformed param will be `undefined`, not `null`.\n",{"id":45,"difficulty":25,"q":46,"a":47},"use-location-fields","What fields does useLocation return and what is each one used for?","**useLocation** returns the current **location object** with five fields: `pathname` (the path string), `search` (the raw query string including `?`), `hash` (the fragment including `#`), `state` (arbitrary data attached by navigate), and `key` (a unique string per history entry).\n\n```jsx\nimport { useLocation } from 'react-router-dom';\n\nfunction DebugLocation() {\n  const location = useLocation();\n  \u002F\u002F location.pathname  → '\u002Fproducts\u002F42'\n  \u002F\u002F location.search    → '?sort=price&order=asc'\n  \u002F\u002F location.hash      → '#reviews'\n  \u002F\u002F location.state     → { from: '\u002Fcart' }  (if set by navigate)\n  \u002F\u002F location.key       → 'default' | 'abc123'\n\n  return \u003Cpre>{JSON.stringify(location, null, 2)}\u003C\u002Fpre>;\n}\n```\n\n**Rule of thumb:** Use `useLocation` to react to URL changes in side effects — put `location` in a `useEffect` dependency array to re-run whenever the user navigates.\n",{"id":49,"difficulty":14,"q":50,"a":51},"use-search-params-read","How do you read query string parameters with useSearchParams?","**useSearchParams** returns a `[searchParams, setSearchParams]` tuple where `searchParams` is a **URLSearchParams** instance. Call `.get('key')` for a single value or `.getAll('key')` for repeated values.\n\n```jsx\nimport { useSearchParams } from 'react-router-dom';\n\nfunction ProductList() {\n  const [searchParams] = useSearchParams();\n\n  const category = searchParams.get('category'); \u002F\u002F 'electronics' | null\n  const page = Number(searchParams.get('page') ?? '1'); \u002F\u002F default to 1\n  const tags = searchParams.getAll('tag'); \u002F\u002F ['sale', 'new']\n\n  return \u003Cp>Showing {category} — page {page}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Always provide a fallback when calling `.get()` — it returns `null` if the parameter is absent, which will cause bugs if you use it as a number or array without checking.\n",{"id":53,"difficulty":14,"q":54,"a":55},"use-search-params-write","How do you update query parameters with useSearchParams without losing existing ones?","Call **setSearchParams** with a callback to get the current params and mutate a copy, or pass a plain object to replace all params. Passing the function form is safer when you only want to update a subset.\n\n```jsx\nfunction SortControl() {\n  const [searchParams, setSearchParams] = useSearchParams();\n\n  function handleSort(field) {\n    setSearchParams(prev => {\n      \u002F\u002F Clone so we don't mutate the live object\n      const next = new URLSearchParams(prev);\n      next.set('sort', field);   \u002F\u002F update one key\n      next.delete('page');       \u002F\u002F reset pagination on sort change\n      return next;\n    });\n  }\n\n  return \u003Cbutton onClick={() => handleSort('price')}>Sort by price\u003C\u002Fbutton>;\n}\n```\n\n**Rule of thumb:** Always use the **callback form** of `setSearchParams` when you need to preserve existing params — passing a plain object silently drops every key you don't include.\n",{"id":57,"difficulty":14,"q":58,"a":59},"use-search-params-replace","How do you control whether setSearchParams pushes or replaces the history entry?","Pass `{ replace: true }` as the **second argument** to `setSearchParams` to replace the current history entry instead of pushing a new one. This is important for filters and sort controls where you don't want the Back button to undo each individual filter change.\n\n```jsx\nfunction FilterPanel() {\n  const [searchParams, setSearchParams] = useSearchParams();\n\n  function setFilter(key, value) {\n    setSearchParams(\n      prev => { const n = new URLSearchParams(prev); n.set(key, value); return n; },\n      { replace: true } \u002F\u002F don't pollute the history stack with every filter tick\n    );\n  }\n\n  return \u003Cinput onChange={e => setFilter('q', e.target.value)} \u002F>;\n}\n```\n\n**Rule of thumb:** Use `replace: true` for search\u002Ffilter inputs that update on every keystroke; use the default push for intentional navigation actions the user might want to undo.\n",{"id":61,"difficulty":14,"q":62,"a":63},"use-match-basic","What does useMatch do and what does it return?","**useMatch** tests whether the current URL matches a given **path pattern** and returns a **match object** with `params`, `pathname`, and `pathnameBase` if it matches, or `null` if it does not. It is useful for styling active links or conditionally rendering UI based on route context.\n\n```jsx\nimport { useMatch } from 'react-router-dom';\n\nfunction NavItem({ to, label }) {\n  \u002F\u002F Returns non-null only when the current URL matches '\u002Fsettings\u002F*'\n  const match = useMatch({ path: to, end: false });\n\n  return (\n    \u003Ca\n      href={to}\n      style={{ fontWeight: match ? 'bold' : 'normal' }} \u002F\u002F highlight active section\n    >\n      {label}\n    \u003C\u002Fa>\n  );\n}\n```\n\n**Rule of thumb:** Use `end: false` to match a prefix (like `\u002Fsettings` matching `\u002Fsettings\u002Fprofile`); use the default `end: true` for exact matches only.\n",{"id":65,"difficulty":66,"q":67,"a":68},"use-routes-dynamic-config","hard","What is useRoutes and when would you choose it over JSX route declarations?","**useRoutes** accepts a **route config array** (the same shape as the `\u003CRoutes>\u002F\u003CRoute>` JSX tree) and returns the matched element or `null`. It is the hook-based equivalent of `\u003CRoutes>` and is useful when route configuration is **data-driven** — loaded from an API, a CMS, or a permissions table.\n\n```jsx\nimport { useRoutes } from 'react-router-dom';\n\nconst routes = [\n  { path: '\u002F', element: \u003CHome \u002F> },\n  {\n    path: '\u002Fdashboard',\n    element: \u003CDashboardLayout \u002F>,\n    children: [\n      { index: true, element: \u003CDashboardHome \u002F> },\n      { path: 'reports', element: \u003CReports \u002F> },\n    ],\n  },\n  { path: '*', element: \u003CNotFound \u002F> },\n];\n\nfunction App() {\n  \u002F\u002F Renders whichever element matches the current URL\n  const element = useRoutes(routes);\n  return element;\n}\n```\n\n**Rule of thumb:** Use `useRoutes` when your route tree is dynamic or generated at runtime; prefer declarative JSX `\u003CRoutes>` for static trees because it is easier to read at a glance.\n",{"id":70,"difficulty":66,"q":71,"a":72},"use-link-click-handler","What is useLinkClickHandler and when would you need it?","**useLinkClickHandler** returns an **onClick handler** for a custom anchor element that performs client-side navigation the same way `\u003CLink>` does — handling modifier keys (Ctrl\u002FCmd-click to open in new tab), `target` attributes, and `event.preventDefault()`. Use it when you need a fully custom link component that cannot extend `\u003CLink>`.\n\n```jsx\nimport { useLinkClickHandler } from 'react-router-dom';\n\n\u002F\u002F A styled anchor that wraps an icon + label but still SPA-navigates\nfunction FancyLink({ to, children, ...rest }) {\n  const handleClick = useLinkClickHandler(to, {\n    replace: false, \u002F\u002F default push behavior\n    state: { from: 'fancy' },\n  });\n\n  return (\n    \u002F\u002F Must be a real \u003Ca> so the browser handles right-click → \"Open in new tab\"\n    \u003Ca href={to} onClick={handleClick} {...rest}>\n      {children}\n    \u003C\u002Fa>\n  );\n}\n```\n\n**Rule of thumb:** Only reach for `useLinkClickHandler` when you cannot use `\u003CLink>` or `\u003CNavLink>` as the root element — for most cases those components are sufficient.\n",{"id":74,"difficulty":14,"q":75,"a":76},"use-href","What does useHref return and how is it different from just using the path string directly?","**useHref** converts a **to** value (relative or absolute path) into a **fully resolved href string** that accounts for any `basename` configured on the router. If your app is mounted at `\u002Fapp`, `useHref('\u002Fdashboard')` returns `\u002Fapp\u002Fdashboard`, whereas a hardcoded string would break.\n\n```jsx\nimport { useHref } from 'react-router-dom';\n\nfunction ExternalShareButton({ to }) {\n  \u002F\u002F Resolves against the router's basename, giving a correct absolute path\n  const href = useHref(to);\n  const fullUrl = `${window.location.origin}${href}`;\n\n  return (\n    \u003Cbutton onClick={() => navigator.clipboard.writeText(fullUrl)}>\n      Copy link\n    \u003C\u002Fbutton>\n  );\n}\n```\n\n**Rule of thumb:** Use `useHref` whenever you need the resolved URL as a string (for clipboard, meta tags, or non-anchor elements) rather than navigating directly — it is the basename-aware alternative to manually concatenating strings.\n",{"id":78,"difficulty":66,"q":79,"a":80},"use-blocker-pattern","How do you block navigation with useBlocker in React Router v6.4+?","**useBlocker** accepts a **condition function** that receives `{ currentLocation, nextLocation, historyAction }` and returns `true` to block. When blocked, it returns a **blocker object** with `state: 'blocked'` and `proceed()`\u002F`reset()` methods to confirm or cancel.\n\n```jsx\nimport { useBlocker } from 'react-router-dom';\n\nfunction EditForm({ isDirty }) {\n  const blocker = useBlocker(\n    \u002F\u002F Block only when there are unsaved changes\n    ({ currentLocation, nextLocation }) =>\n      isDirty && currentLocation.pathname !== nextLocation.pathname\n  );\n\n  return (\n    \u003C>\n      \u003Cform>...\u003C\u002Fform>\n      {blocker.state === 'blocked' && (\n        \u003Cdialog open>\n          \u003Cp>You have unsaved changes. Leave anyway?\u003C\u002Fp>\n          \u003Cbutton onClick={blocker.proceed}>Leave\u003C\u002Fbutton>   {\u002F* confirm *\u002F}\n          \u003Cbutton onClick={blocker.reset}>Stay\u003C\u002Fbutton>       {\u002F* cancel *\u002F}\n        \u003C\u002Fdialog>\n      )}\n    \u003C\u002F>\n  );\n}\n```\n\n**Rule of thumb:** Always call either `blocker.proceed()` or `blocker.reset()` to resolve a blocked navigation — leaving the blocker in `'blocked'` state permanently locks the app.\n",{"id":82,"difficulty":14,"q":83,"a":84},"navigate-in-render-mistake","What is wrong with calling navigate() directly inside the render function of a component?","Calling `navigate()` **during render** triggers a navigation while React is still rendering, causing an immediate re-render loop and the React warning \"Cannot update a component while rendering a different component.\" Navigation must always happen in **event handlers** or inside a `useEffect`.\n\n```jsx\n\u002F\u002F ❌ Wrong — navigate called during render causes infinite re-render\nfunction RedirectIfLoggedOut({ user }) {\n  const navigate = useNavigate();\n  if (!user) navigate('\u002Flogin'); \u002F\u002F fires on every render pass\n  return \u003CDashboard \u002F>;\n}\n\n\u002F\u002F ✅ Correct — navigate called inside useEffect, runs after render\nfunction RedirectIfLoggedOut({ user }) {\n  const navigate = useNavigate();\n  useEffect(() => {\n    if (!user) navigate('\u002Flogin', { replace: true });\n  }, [user, navigate]);\n  return user ? \u003CDashboard \u002F> : null;\n}\n```\n\n**Rule of thumb:** If you find yourself writing `navigate()` outside of an event handler or `useEffect`, stop — you are almost certainly in a render path and will cause an infinite loop.\n",{"id":86,"difficulty":14,"q":87,"a":88},"navigate-vs-history-back","What is the difference between navigate(-1) and window.history.back()?","Both navigate back one history entry, but `navigate(-1)` goes through **React Router's transition system** while `window.history.back()` is a raw browser API call. This means React Router's **blockers**, **scroll restoration**, and **transition listeners** fire for `navigate(-1)` but are bypassed by `window.history.back()`.\n\n```jsx\nfunction BackButton() {\n  const navigate = useNavigate();\n\n  return (\n    \u003C>\n      {\u002F* ✅ Preferred — triggers React Router's blockers and listeners *\u002F}\n      \u003Cbutton onClick={() => navigate(-1)}>Back (React Router)\u003C\u002Fbutton>\n\n      {\u002F* ❌ Avoid — skips useBlocker, no transition event, hard to test *\u002F}\n      \u003Cbutton onClick={() => window.history.back()}>Back (native)\u003C\u002Fbutton>\n    \u003C\u002F>\n  );\n}\n```\n\n**Rule of thumb:** Always use `navigate(-1)` in React Router apps so any registered blockers (unsaved-form guards) and navigation listeners fire correctly.\n",{"id":90,"difficulty":14,"q":91,"a":92},"location-state-persist","How does location.state persist across navigations and what are its limitations?","**location.state** is stored in the **browser's History API entry** for that specific navigation. It persists as long as the user stays in the session history — navigating back then forward re-exposes the same state. However, it is **lost on a hard refresh** (F5) and is **not visible in the URL**, so it cannot be bookmarked or shared.\n\n```jsx\n\u002F\u002F Send state when navigating to a list item\nnavigate(`\u002Forders\u002F${id}`, { state: { returnPath: '\u002Forders', filters } });\n\n\u002F\u002F Read state on the destination page\nfunction OrderDetail() {\n  const { state } = useLocation();\n  const returnPath = state?.returnPath ?? '\u002F'; \u002F\u002F graceful fallback for direct visits\n\n  return (\n    \u003Cbutton onClick={() => navigate(returnPath, { state: state?.filters })}>\n      ← Back to orders\n    \u003C\u002Fbutton>\n  );\n}\n```\n\n**Rule of thumb:** Use `location.state` for ephemeral UI data (breadcrumb hints, scroll position, pre-filled values); always provide a sensible fallback for when the user arrives directly via a bookmarked URL.\n",{"id":94,"difficulty":66,"q":95,"a":96},"use-params-optional-segment","How do you handle optional URL parameters when using useParams?","React Router v6 does not support the `?` optional segment syntax directly. The idiomatic approach is to declare **two sibling routes** — one with the segment and one without — and let each render the same component. Inside the component, `useParams` returns `undefined` for the missing key when the shorter route matches.\n\n```jsx\n\u002F\u002F Route config — two routes, one component\n\u003CRoutes>\n  \u003CRoute path=\"\u002Freports\u002F:year\u002F:month\" element={\u003CReportPage \u002F>} \u002F>\n  \u003CRoute path=\"\u002Freports\u002F:year\"        element={\u003CReportPage \u002F>} \u002F>\n\u003C\u002FRoutes>\n\nfunction ReportPage() {\n  const { year, month } = useParams();\n  \u002F\u002F month is undefined when only \u002Freports\u002F2026 is visited\n  const label = month ? `${year}\u002F${month}` : `${year} (all months)`;\n\n  return \u003Ch1>{label}\u003C\u002Fh1>;\n}\n```\n\n**Rule of thumb:** Avoid trying to make a single route handle both cases with regex tricks — two explicit routes are clearer, more maintainable, and easier to test.\n",{"id":98,"difficulty":14,"q":99,"a":100},"use-search-params-vs-state","When should you use useSearchParams instead of useState for managing filter values?","Use **useSearchParams** when the filter state should be **shareable, bookmarkable, or survive a refresh** — the URL is the source of truth. Use `useState` for purely ephemeral UI state (dropdown open\u002Fclose, hover effects) that has no meaning outside the current render.\n\n```jsx\n\u002F\u002F ✅ useSearchParams — user can share\u002Fbookmark filtered results\nfunction ProductFilter() {\n  const [searchParams, setSearchParams] = useSearchParams();\n  const category = searchParams.get('category') ?? 'all';\n\n  return (\n    \u003Cselect\n      value={category}\n      onChange={e =>\n        setSearchParams({ category: e.target.value }, { replace: true })\n      }\n    >\n      \u003Coption value=\"all\">All\u003C\u002Foption>\n      \u003Coption value=\"books\">Books\u003C\u002Foption>\n    \u003C\u002Fselect>\n  );\n}\n```\n\n**Rule of thumb:** If a user refreshing the page or copying the URL should see the same results, that state belongs in the URL (`useSearchParams`); if it is purely visual\u002Ftransient, `useState` is sufficient.\n",{"id":102,"difficulty":14,"q":103,"a":104},"use-match-end-option","What does the end option in useMatch control and when should you set it to false?","The **end** option (default `true`) controls whether the pattern must match the **entire pathname** or just a **prefix**. With `end: true`, `\u002Fsettings` only matches the exact path `\u002Fsettings`. With `end: false`, it matches `\u002Fsettings`, `\u002Fsettings\u002Fprofile`, `\u002Fsettings\u002Fbilling`, and so on — useful for highlighting a parent nav item when any child route is active.\n\n```jsx\nfunction SettingsNav() {\n  \u002F\u002F end: false — active whenever any \u002Fsettings\u002F* route is rendered\n  const settingsMatch = useMatch({ path: '\u002Fsettings', end: false });\n  \u002F\u002F end: true (default) — active only on the exact \u002Fsettings page\n  const exactMatch  = useMatch('\u002Fsettings');\n\n  return (\n    \u003Cnav>\n      \u003Ca style={{ fontWeight: settingsMatch ? 'bold' : 'normal' }}\n         href=\"\u002Fsettings\">\n        Settings {settingsMatch && '(active section)'}\n      \u003C\u002Fa>\n    \u003C\u002Fnav>\n  );\n}\n```\n\n**Rule of thumb:** Set `end: false` for top-level nav items that have child routes; use the default `end: true` (or just pass the path string) for leaf routes where exact matching matters.\n",20,null,{"description":11},"React Router v6 navigation hooks interview questions — useNavigate, useParams, useLocation, useSearchParams, useMatch, useBlocker, and programmatic navigation patterns.","react\u002Frouting\u002Fnavigation-hooks","Routing","routing","2026-06-24","abQ4KFyCLOvgdO16RQn_HXrPwd96ieDJPJmP7kOkrlQ",[115,119,122,123],{"subtopic":116,"path":117,"order":118},"Routing Basics","\u002Freact\u002Frouting\u002Frouting-basics",1,{"subtopic":120,"path":121,"order":12},"Dynamic and Nested Routes","\u002Freact\u002Frouting\u002Fdynamic-nested-routes",{"subtopic":6,"path":21,"order":20},{"subtopic":124,"path":125,"order":126},"Protected Routes","\u002Freact\u002Frouting\u002Fprotected-routes",4,{"path":128,"title":129},"\u002Fblog\u002Freact-navigation-hooks-guide","React Router v6 Navigation Hooks — Complete Interview Guide",1782244101044]