[{"data":1,"prerenderedAt":411},["ShallowReactive",2],{"topic-react-routing":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Freact.yml","React interview questions on hooks, components, state and rendering — the most-requested front-end framework in technical interviews.","yml","react",{},"React",2,"frameworks\u002Freact",1,"RA6wemU0xFHrA1ZKCABP1J7MireA3Bt_FCJMOuMgsm0",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Freact-routing.yml","React Router v6 fundamentals, dynamic and nested routes, navigation hooks (useNavigate, useParams, useLocation), and protecting routes with auth guards.",{},"Routing",5,"routing","topics\u002Freact-routing","uQtCZwNBL9038tX4cckiGrc-kZEhtoaYkz3lhgW1jU0",[25,128,223,317],{"id":26,"title":27,"body":28,"description":32,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":36,"navigation":37,"order":13,"path":38,"questions":39,"questionsCount":121,"related":122,"seo":123,"seoDescription":124,"stem":125,"subtopic":27,"topic":19,"topicSlug":21,"updated":126,"__hash__":127},"qa\u002Freact\u002Frouting\u002Frouting-basics.md","Routing Basics",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":11,"depth":11,"links":33},"",[],"easy","md",{},true,"\u002Freact\u002Frouting\u002Frouting-basics",[40,44,48,53,57,61,65,69,73,77,81,85,89,93,97,101,105,109,113,117],{"id":41,"difficulty":34,"q":42,"a":43},"what-is-client-side-routing","What is client-side routing and why does React use it?","**Client-side routing** means the browser never performs a full page reload when navigating between views. Instead, JavaScript intercepts navigation events, updates the URL via the **History API**, and re-renders the appropriate component tree — giving the feel of a multi-page app without the network round-trip.\n\n```jsx\n\u002F\u002F Without client-side routing: browser fetches \u002Fabout from the server\n\u002F\u002F With client-side routing: JS swaps components and pushes to history\nimport { Link } from 'react-router-dom';\n\nfunction Nav() {\n  return (\n    \u002F\u002F Clicking this never triggers a server request\n    \u003CLink to=\"\u002Fabout\">About\u003C\u002FLink>\n  );\n}\n```\n\n**Rule of thumb:** Use client-side routing in any React SPA so users get instant navigation and the app shell (header, sidebar) stays mounted across route changes.\n",{"id":45,"difficulty":34,"q":46,"a":47},"install-react-router-v6","How do you install and minimally set up React Router v6?","Install the package, then wrap your app in **BrowserRouter** (or use **createBrowserRouter** for the data router API). Every router hook and component must live inside that provider.\n\n```jsx\n\u002F\u002F 1. npm install react-router-dom\n\n\u002F\u002F 2. main.jsx — wrap the root\nimport { BrowserRouter } from 'react-router-dom';\nimport { createRoot } from 'react-dom\u002Fclient';\nimport App from '.\u002FApp';\n\ncreateRoot(document.getElementById('root')).render(\n  \u003CBrowserRouter>   {\u002F* provides routing context to the whole tree *\u002F}\n    \u003CApp \u002F>\n  \u003C\u002FBrowserRouter>\n);\n```\n\n**Rule of thumb:** Always place `BrowserRouter` (or its equivalent) at the very root of your component tree, not inside a page component — otherwise hooks like `useNavigate` will throw a context error.\n",{"id":49,"difficulty":50,"q":51,"a":52},"create-browser-router-api","medium","What is createBrowserRouter and how does it differ from BrowserRouter?","**createBrowserRouter** is the v6.4+ **data router** API. It co-locates routes with **loaders** and **actions** (server-style data fetching\u002Fmutation), and it is the recommended approach for new apps. **BrowserRouter** is the legacy component wrapper that does not support loaders\u002Factions.\n\n```jsx\nimport { createBrowserRouter, RouterProvider } from 'react-router-dom';\nimport Root from '.\u002FRoot';\nimport Home from '.\u002FHome';\nimport About from '.\u002FAbout';\n\nconst router = createBrowserRouter([\n  {\n    path: '\u002F',\n    element: \u003CRoot \u002F>,   \u002F\u002F layout with \u003COutlet \u002F>\n    children: [\n      { index: true, element: \u003CHome \u002F> },\n      { path: 'about', element: \u003CAbout \u002F> },\n    ],\n  },\n]);\n\n\u002F\u002F main.jsx\n\u003CRouterProvider router={router} \u002F>\n```\n\n**Rule of thumb:** Reach for `createBrowserRouter` in greenfield projects; use `BrowserRouter` only when you need to stay on the simpler JSX-only API or are migrating incrementally.\n",{"id":54,"difficulty":34,"q":55,"a":56},"route-and-routes-components","What are the Routes and Route components, and how do they work together?","**Routes** is a container that picks the single best-matching **Route** from its children. **Route** declares a `path` and the `element` to render when the URL matches. In v6, `Routes` replaces the v5 `Switch` and always does **exclusive** (first-match) selection.\n\n```jsx\nimport { Routes, Route } from 'react-router-dom';\nimport Home from '.\u002FHome';\nimport About from '.\u002FAbout';\nimport NotFound from '.\u002FNotFound';\n\nfunction App() {\n  return (\n    \u003CRoutes>\n      \u003CRoute path=\"\u002F\" element={\u003CHome \u002F>} \u002F>\n      \u003CRoute path=\"\u002Fabout\" element={\u003CAbout \u002F>} \u002F>\n      \u003CRoute path=\"*\" element={\u003CNotFound \u002F>} \u002F>  {\u002F* catch-all *\u002F}\n    \u003C\u002FRoutes>\n  );\n}\n```\n\n**Rule of thumb:** Always keep your top-level `Routes` block in one place (usually `App.jsx`) so route precedence is obvious at a glance.\n",{"id":58,"difficulty":34,"q":59,"a":60},"exact-matching-v6","How does path matching work in React Router v6 — is there an \"exact\" prop?","In v6 all routes match **exactly** by default — the `exact` prop was removed. A route with `path=\"\u002F\"` will **not** match `\u002Fabout`; you no longer need to add `exact` everywhere. The router also ranks routes by specificity, so more-specific paths win over less-specific ones.\n\n```jsx\n\u003CRoutes>\n  {\u002F* v5 needed exact on \"\u002F\", v6 does not *\u002F}\n  \u003CRoute path=\"\u002F\"        element={\u003CHome \u002F>} \u002F>   {\u002F* only \"\u002F\" *\u002F}\n  \u003CRoute path=\"\u002Fusers\"   element={\u003CUsers \u002F>} \u002F>  {\u002F* only \"\u002Fusers\" *\u002F}\n  \u003CRoute path=\"\u002Fusers\u002F:id\" element={\u003CUser \u002F>} \u002F> {\u002F* wins over \"\u002Fusers\" for \"\u002Fusers\u002F42\" *\u002F}\n\u003C\u002FRoutes>\n```\n\n**Rule of thumb:** Remove all `exact` props when migrating from v5 to v6 — they are a no-op and will generate a warning.\n",{"id":62,"difficulty":50,"q":63,"a":64},"nested-routes","How do nested routes work in React Router v6?","Nest **Route** elements inside a parent **Route** to model layout nesting. The parent renders its `element` and places an **Outlet** component where child routes should appear. The child path is **relative** to the parent — no leading slash needed.\n\n```jsx\n\u003CRoutes>\n  \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboardLayout \u002F>}>\n    {\u002F* relative paths — resolved as \u002Fdashboard\u002Foverview *\u002F}\n    \u003CRoute path=\"overview\"  element={\u003COverview \u002F>} \u002F>\n    \u003CRoute path=\"settings\"  element={\u003CSettings \u002F>} \u002F>\n  \u003C\u002FRoute>\n\u003C\u002FRoutes>\n\n\u002F\u002F DashboardLayout.jsx\nimport { Outlet } from 'react-router-dom';\n\nfunction DashboardLayout() {\n  return (\n    \u003Cdiv>\n      \u003CSidebar \u002F>\n      \u003Cmain>\n        \u003COutlet \u002F>  {\u002F* child route renders here *\u002F}\n      \u003C\u002Fmain>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Use nested routes whenever multiple pages share a persistent layout (nav, sidebar, header) — the layout component stays mounted while only the `Outlet` content swaps.\n",{"id":66,"difficulty":50,"q":67,"a":68},"outlet-component","What is the Outlet component and when is it required?","**Outlet** is a placeholder rendered by a parent route's `element`; it marks the spot where the matched child route's component should appear. Without `Outlet`, child routes match in the URL but their elements are never rendered to the screen.\n\n```jsx\nimport { Outlet, NavLink } from 'react-router-dom';\n\nfunction AdminLayout() {\n  return (\n    \u003Cdiv className=\"admin\">\n      \u003Cnav>\n        \u003CNavLink to=\"users\">Users\u003C\u002FNavLink>\n        \u003CNavLink to=\"reports\">Reports\u003C\u002FNavLink>\n      \u003C\u002Fnav>\n      {\u002F* matched child route appears here *\u002F}\n      \u003COutlet \u002F>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Every route that has children in the route tree must render `\u003COutlet \u002F>` somewhere in its element, or those children will silently not appear.\n",{"id":70,"difficulty":50,"q":71,"a":72},"index-routes","What is an index route and when should you use one?","An **index route** is a child route with `index={true}` instead of a `path`. It renders inside the parent's `Outlet` when the URL exactly matches the parent path — serving as the \"default child\" view.\n\n```jsx\n\u003CRoutes>\n  \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboardLayout \u002F>}>\n    {\u002F* renders at \u002Fdashboard when no child segment follows *\u002F}\n    \u003CRoute index element={\u003CDashboardHome \u002F>} \u002F>\n    \u003CRoute path=\"users\"   element={\u003CUsers \u002F>} \u002F>\n    \u003CRoute path=\"reports\" element={\u003CReports \u002F>} \u002F>\n  \u003C\u002FRoute>\n\u003C\u002FRoutes>\n```\n\n**Rule of thumb:** Add an index route to every layout route so the `Outlet` is never empty — without it, visiting the parent path exactly renders a blank content area.\n",{"id":74,"difficulty":34,"q":75,"a":76},"link-component","What is the Link component and why should you use it instead of an anchor tag?","**Link** renders an `\u003Ca>` element but intercepts the click, calls `history.pushState`, and re-renders the app without a page reload. A plain `\u003Ca href=\"...\">` would trigger a full browser navigation, destroying React state.\n\n```jsx\nimport { Link } from 'react-router-dom';\n\nfunction Nav() {\n  return (\n    \u003Cnav>\n      \u003CLink to=\"\u002F\">Home\u003C\u002FLink>           {\u002F* absolute path *\u002F}\n      \u003CLink to=\"settings\">Settings\u003C\u002FLink> {\u002F* relative to current route *\u002F}\n      \u003CLink to=\"\u002Fusers\u002F42\">User 42\u003C\u002FLink>\n    \u003C\u002Fnav>\n  );\n}\n```\n\n**Rule of thumb:** Always use `Link` (or `NavLink`) for in-app navigation; reserve plain `\u003Ca>` tags only for external URLs or files that require a real browser navigation.\n",{"id":78,"difficulty":50,"q":79,"a":80},"navlink-component","How does NavLink differ from Link, and how do you style the active state in v6?","**NavLink** is a `Link` that knows whether its destination is the current route. In v6 it automatically applies an `\"active\"` CSS class (and optionally `\"pending\"`) when matched. The v5 `activeClassName` \u002F `activeStyle` props were removed; instead, pass a **callback** to `className` or `style` that receives `{ isActive }`.\n\n```jsx\nimport { NavLink } from 'react-router-dom';\n\nfunction Nav() {\n  return (\n    \u003Cnav>\n      \u003CNavLink\n        to=\"\u002Fdashboard\"\n        className={({ isActive }) =>\n          isActive ? 'nav-link active' : 'nav-link'\n        }\n      >\n        Dashboard\n      \u003C\u002FNavLink>\n\n      {\u002F* or inline style callback *\u002F}\n      \u003CNavLink\n        to=\"\u002Fsettings\"\n        style={({ isActive }) => ({ fontWeight: isActive ? 700 : 400 })}\n      >\n        Settings\n      \u003C\u002FNavLink>\n    \u003C\u002Fnav>\n  );\n}\n```\n\n**Rule of thumb:** Use `NavLink` for navigation menus where visual feedback on the active item matters; use plain `Link` everywhere else.\n",{"id":82,"difficulty":50,"q":83,"a":84},"use-navigate-hook","How do you programmatically navigate in React Router v6?","The **useNavigate** hook returns a `navigate` function. Call it with a path string to push a new entry, or pass `{ replace: true }` to replace the current history entry (useful after form submissions). Negative numbers (`navigate(-1)`) go back in history.\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(formData);\n    \u002F\u002F push to dashboard after successful login\n    navigate('\u002Fdashboard');\n    \u002F\u002F or replace so the user can't go \"back\" to the login page:\n    \u002F\u002F navigate('\u002Fdashboard', { replace: true });\n  }\n\n  return \u003Cform onSubmit={handleSubmit}>...\u003C\u002Fform>;\n}\n```\n\n**Rule of thumb:** Use `navigate(path, { replace: true })` after any action that should not remain in the browser's history stack (login redirects, post-submit confirmations).\n",{"id":86,"difficulty":34,"q":87,"a":88},"catch-all-404-route","How do you create a 404 \u002F catch-all route in React Router v6?","Add a `\u003CRoute path=\"*\">` as the last child of your `Routes`. The wildcard `*` matches any URL not claimed by earlier routes, making it the conventional 404 handler.\n\n```jsx\nimport NotFound from '.\u002FNotFound';\n\nfunction App() {\n  return (\n    \u003CRoutes>\n      \u003CRoute path=\"\u002F\"       element={\u003CHome \u002F>} \u002F>\n      \u003CRoute path=\"\u002Fabout\"  element={\u003CAbout \u002F>} \u002F>\n      \u003CRoute path=\"\u002Fusers\u002F:id\" element={\u003CUserProfile \u002F>} \u002F>\n      {\u002F* catches everything else *\u002F}\n      \u003CRoute path=\"*\" element={\u003CNotFound \u002F>} \u002F>\n    \u003C\u002FRoutes>\n  );\n}\n```\n\n**Rule of thumb:** Always include a `path=\"*\"` route — without it, unmatched URLs render nothing, which silently looks broken to users.\n",{"id":90,"difficulty":50,"q":91,"a":92},"history-mode-vs-hash-mode","What is the difference between BrowserRouter (history mode) and HashRouter (hash mode)?","**BrowserRouter** uses the HTML5 **History API** (`pushState`) and produces clean URLs like `\u002Fabout`. It requires the server to serve `index.html` for every path. **HashRouter** encodes the route in the URL hash (`\u002F#\u002Fabout`) — the hash is never sent to the server, so it works on any static host without server config.\n\n```jsx\n\u002F\u002F History mode — clean URLs, needs server catch-all\nimport { BrowserRouter } from 'react-router-dom';\n\u003CBrowserRouter>\u003CApp \u002F>\u003C\u002FBrowserRouter>\n\n\u002F\u002F Hash mode — works on GitHub Pages \u002F plain file servers\nimport { HashRouter } from 'react-router-dom';\n\u003CHashRouter>\u003CApp \u002F>\u003C\u002FHashRouter>\n\n\u002F\u002F Server catch-all example (Express)\n\u002F\u002F app.get('*', (req, res) => res.sendFile('index.html'));\n```\n\n**Rule of thumb:** Prefer `BrowserRouter` with a server catch-all for production apps; use `HashRouter` only when you have no control over server configuration (e.g., GitHub Pages, local `file:\u002F\u002F` serving).\n",{"id":94,"difficulty":50,"q":95,"a":96},"scroll-restoration","How does scroll restoration work with React Router v6?","By default browsers try to restore scroll position, but client-side navigation breaks this because the DOM updates asynchronously. React Router v6 ships a **ScrollRestoration** component (data router only) that saves and restores scroll position per URL entry. For `BrowserRouter` apps, a common manual solution is a `ScrollToTop` component that calls `window.scrollTo(0, 0)` on route change.\n\n```jsx\n\u002F\u002F Data router approach (createBrowserRouter)\nimport { ScrollRestoration } from 'react-router-dom';\n\nfunction Root() {\n  return (\n    \u003C>\n      \u003CNav \u002F>\n      \u003COutlet \u002F>\n      \u003CScrollRestoration \u002F>  {\u002F* place once in the root layout *\u002F}\n    \u003C\u002F>\n  );\n}\n\n\u002F\u002F Manual approach for BrowserRouter\nimport { useEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\n\nfunction ScrollToTop() {\n  const { pathname } = useLocation();\n  useEffect(() => { window.scrollTo(0, 0); }, [pathname]);\n  return null;\n}\n```\n\n**Rule of thumb:** Always handle scroll restoration explicitly — without it, users navigating back to a long page will be stranded at the bottom.\n",{"id":98,"difficulty":50,"q":99,"a":100},"relative-paths-nested-routes","What is the gotcha with relative paths in nested routes?","Child route `path` values inside a `\u003CRoute>` parent are **relative** — do not add a leading slash. A leading slash makes the path absolute, which breaks nesting because it is resolved from the root instead of relative to the parent.\n\n```jsx\n\u002F\u002F WRONG — leading slash makes children absolute paths\n\u003CRoute path=\"\u002Fsettings\" element={\u003CSettingsLayout \u002F>}>\n  \u003CRoute path=\"\u002Fsettings\u002Fprofile\"  element={\u003CProfile \u002F>} \u002F>  {\u002F* breaks *\u002F}\n  \u003CRoute path=\"\u002Fsettings\u002Fsecurity\" element={\u003CSecurity \u002F>} \u002F> {\u002F* breaks *\u002F}\n\u003C\u002FRoute>\n\n\u002F\u002F CORRECT — relative paths, no leading slash\n\u003CRoute path=\"\u002Fsettings\" element={\u003CSettingsLayout \u002F>}>\n  \u003CRoute path=\"profile\"  element={\u003CProfile \u002F>} \u002F>  {\u002F* \u002Fsettings\u002Fprofile *\u002F}\n  \u003CRoute path=\"security\" element={\u003CSecurity \u002F>} \u002F> {\u002F* \u002Fsettings\u002Fsecurity *\u002F}\n\u003C\u002FRoute>\n```\n\n**Rule of thumb:** Never prefix child route paths with `\u002F` — keep them relative so React Router can compose parent + child segments correctly.\n",{"id":102,"difficulty":34,"q":103,"a":104},"use-location-hook","What does the useLocation hook return and when would you use it?","**useLocation** returns the current **location object** with `pathname`, `search`, `hash`, `state`, and `key`. It is useful for reading query params, animating on route change, or passing state between routes without putting it in the URL.\n\n```jsx\nimport { useLocation } from 'react-router-dom';\n\nfunction Analytics() {\n  const location = useLocation();\n\n  useEffect(() => {\n    \u002F\u002F fire a page-view event on every navigation\n    trackPageView(location.pathname);\n  }, [location.pathname]);\n\n  return null;\n}\n\n\u002F\u002F Passing state via Link and reading it with useLocation\n\u003CLink to=\"\u002Fconfirm\" state={{ orderId: 42 }}>Confirm\u003C\u002FLink>\n\nfunction ConfirmPage() {\n  const { state } = useLocation();\n  return \u003Cp>Order {state?.orderId} confirmed\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Use `useLocation` whenever you need to react to URL changes without controlling the route — analytics, animations, and reading navigation state are the classic cases.\n",{"id":106,"difficulty":34,"q":107,"a":108},"use-params-hook","How do you read dynamic URL segments in React Router v6?","Declare a dynamic segment in the route path with a colon prefix (`:paramName`). Inside the matched component, read it with the **useParams** hook, which returns an object keyed by every named segment in the route.\n\n```jsx\n\u002F\u002F Route declaration\n\u003CRoute path=\"\u002Fusers\u002F:userId\u002Fposts\u002F:postId\" element={\u003CPost \u002F>} \u002F>\n\n\u002F\u002F Component\nimport { useParams } from 'react-router-dom';\n\nfunction Post() {\n  const { userId, postId } = useParams();\n  \u002F\u002F userId and postId are always strings — convert if needed\n  return \u003Cp>User {userId}, Post {postId}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Always coerce URL params to the correct type (`Number(userId)`) before using them in logic — `useParams` always returns strings regardless of what you stored.\n",{"id":110,"difficulty":50,"q":111,"a":112},"navigate-replace-vs-push","When should you use navigate with replace: true versus a normal push?","A normal `navigate(path)` **pushes** a new entry onto the history stack, so the user can press Back to return. `navigate(path, { replace: true })` **replaces** the current entry, removing it from the stack so Back skips it.\n\n```jsx\nimport { useNavigate } from 'react-router-dom';\n\nfunction CheckoutSuccess() {\n  const navigate = useNavigate();\n\n  \u002F\u002F After payment, replace history so pressing Back\n  \u002F\u002F doesn't return to the payment form\n  useEffect(() => {\n    navigate('\u002Forder-confirmation', { replace: true });\n  }, []);\n\n  return null;\n}\n\n\u002F\u002F Also useful for auth redirects:\n\u002F\u002F navigate('\u002Flogin', { replace: true });\n\u002F\u002F — user can't go \"back\" to the protected page after logout\n```\n\n**Rule of thumb:** Use `replace: true` after any action that should be \"final\" in the history stack — payment success, login\u002Flogout, and post-submission confirmations are the standard cases.\n",{"id":114,"difficulty":34,"q":115,"a":116},"link-to-prop-types","What values can you pass to the \"to\" prop of Link?","The `to` prop accepts a **string** (path, with optional query\u002Fhash), a **partial location object**, or a **relative string**. Relative paths are resolved against the current route's URL segment, making them useful inside nested route trees.\n\n```jsx\nimport { Link } from 'react-router-dom';\n\nfunction Examples() {\n  return (\n    \u003C>\n      {\u002F* absolute string *\u002F}\n      \u003CLink to=\"\u002Fusers\">Users\u003C\u002FLink>\n\n      {\u002F* string with query + hash *\u002F}\n      \u003CLink to=\"\u002Fsearch?q=react#results\">Search\u003C\u002FLink>\n\n      {\u002F* location object for programmatic control *\u002F}\n      \u003CLink to={{ pathname: '\u002Fusers', search: '?page=2' }}>Page 2\u003C\u002FLink>\n\n      {\u002F* relative — goes up one segment then to \"edit\" *\u002F}\n      \u003CLink to=\"..\u002Fedit\">Edit\u003C\u002FLink>\n    \u003C\u002F>\n  );\n}\n```\n\n**Rule of thumb:** Prefer string `to` values for simplicity; switch to the object form only when you need to set `search`, `hash`, or `state` independently.\n",{"id":118,"difficulty":34,"q":119,"a":120},"routes-outside-router","What error do you get when you use a React Router hook outside of a Router, and how do you fix it?","Any React Router hook (`useNavigate`, `useLocation`, `useParams`, etc.) throws **\"useNavigate() may be used only in the context of a Router component\"** (or similar) when called outside the `BrowserRouter` \u002F `RouterProvider` tree. The fix is always to ensure the Router wraps the component tree at or above the point where the hook is called.\n\n```jsx\n\u002F\u002F WRONG — hook runs outside the Router tree\nfunction App() {\n  const navigate = useNavigate(); \u002F\u002F throws!\n  return \u003CBrowserRouter>...\u003C\u002FBrowserRouter>;\n}\n\n\u002F\u002F CORRECT — Router wraps everything that uses routing hooks\nfunction Root() {\n  return (\n    \u003CBrowserRouter>\n      \u003CApp \u002F>  {\u002F* App and all descendants can now use hooks *\u002F}\n    \u003C\u002FBrowserRouter>\n  );\n}\n\nfunction App() {\n  const navigate = useNavigate(); \u002F\u002F works fine here\n  return \u003CRoutes>...\u003C\u002FRoutes>;\n}\n```\n\n**Rule of thumb:** If you see a Router context error, trace the component tree upward — the component calling the hook is not a descendant of any `BrowserRouter` or `RouterProvider`.\n",20,null,{"description":32},"React Router v6 routing basics interview questions — BrowserRouter, Route, Link, NavLink, Outlet, nested routes, index routes, useNavigate, and catch-all 404 routes.","react\u002Frouting\u002Frouting-basics","2026-06-24","aWZVzujf-dZeSv_aUKLbBl_U0vburev-ArANL2qZ1MU",{"id":129,"title":130,"body":131,"description":32,"difficulty":50,"extension":35,"framework":10,"frameworkSlug":8,"meta":135,"navigation":37,"order":11,"path":136,"questions":137,"questionsCount":121,"related":122,"seo":218,"seoDescription":219,"stem":220,"subtopic":221,"topic":19,"topicSlug":21,"updated":126,"__hash__":222},"qa\u002Freact\u002Frouting\u002Fdynamic-nested-routes.md","Dynamic Nested Routes",{"type":29,"value":132,"toc":133},[],{"title":32,"searchDepth":11,"depth":11,"links":134},[],{},"\u002Freact\u002Frouting\u002Fdynamic-nested-routes",[138,142,146,150,154,158,162,166,169,173,177,181,185,189,193,198,202,206,210,214],{"id":139,"difficulty":34,"q":140,"a":141},"dynamic-segment-syntax","How do you define a dynamic URL segment in React Router v6, and how do you read it inside the component?","A **dynamic segment** is a path token prefixed with `:`. React Router captures whatever the user types in that position and makes it available via **`useParams()`**, which returns an object keyed by segment name.\n\n```jsx\n\u002F\u002F Route definition\n\u003CRoute path=\"\u002Fusers\u002F:userId\" element={\u003CUserProfile \u002F>} \u002F>\n\n\u002F\u002F Inside UserProfile\nimport { useParams } from 'react-router-dom';\n\nfunction UserProfile() {\n  const { userId } = useParams(); \u002F\u002F e.g. \"42\"\n  \u002F\u002F userId is always a string — coerce if you need a number\n  return \u003Ch1>User {userId}\u003C\u002Fh1>;\n}\n```\n\n**Rule of thumb:** Name segments descriptively (`:userId`, not `:id`) so reading `useParams()` reads like documentation.\n",{"id":143,"difficulty":34,"q":144,"a":145},"params-always-strings","What is the type of a route param returned by useParams, and what bug does that cause?","**`useParams()` always returns strings**, even when the URL contains what looks like a number. This is a common source of subtle bugs when comparing params with strict equality (`===`) against numbers.\n\n```jsx\nfunction PostDetail() {\n  const { postId } = useParams(); \u002F\u002F \"5\" — a string\n\n  \u002F\u002F Bug: 5 === \"5\" is false in JavaScript\n  const post = posts.find(p => p.id === postId); \u002F\u002F undefined!\n\n  \u002F\u002F Fix: coerce before comparing\n  const post2 = posts.find(p => p.id === Number(postId));\n\n  return \u003Cdiv>{post2?.title}\u003C\u002Fdiv>;\n}\n```\n\n**Rule of thumb:** Always coerce params at the top of the component — `Number(id)`, `parseInt(id, 10)`, or validate with Zod — before passing them to any data layer.\n",{"id":147,"difficulty":34,"q":148,"a":149},"multiple-dynamic-segments","Can a single route path contain more than one dynamic segment? Give an example.","Yes. A path can contain **multiple dynamic segments**, each with a unique name. `useParams()` returns all of them in one object.\n\n```jsx\n\u002F\u002F Route definition — two segments\n\u003CRoute path=\"\u002Forgs\u002F:orgId\u002Frepos\u002F:repoId\" element={\u003CRepoDetail \u002F>} \u002F>\n\nfunction RepoDetail() {\n  const { orgId, repoId } = useParams();\n  \u002F\u002F e.g. orgId = \"acme\", repoId = \"dashboard\"\n\n  return (\n    \u003Cp>\n      Org: {orgId} \u002F Repo: {repoId}\n    \u003C\u002Fp>\n  );\n}\n```\n\n**Rule of thumb:** Destructure all params at once at the top of the component; if you need more than three segments, reconsider whether a flatter URL design would be clearer.\n",{"id":151,"difficulty":50,"q":152,"a":153},"splat-routes","What is a splat route in React Router v6 and when would you use one?","A **splat route** (also called a catch-all) uses `*` as the final segment. It matches everything after that point, and the captured string is available as `params[\"*\"]`. It is most often used for **custom 404 pages** or **legacy URL migration**.\n\n```jsx\n\u002F\u002F Catch-all at the root level — renders for any unmatched path\n\u003CRoute path=\"*\" element={\u003CNotFound \u002F>} \u002F>\n\n\u002F\u002F Catch-all inside a layout — only unmatched paths under \u002Fdocs\n\u003CRoute path=\"\u002Fdocs\">\n  \u003CRoute index element={\u003CDocsHome \u002F>} \u002F>\n  \u003CRoute path=\"*\" element={\u003CDocsFallback \u002F>} \u002F>\n\u003C\u002FRoute>\n\nfunction DocsFallback() {\n  const params = useParams();\n  \u002F\u002F params[\"*\"] = \"api\u002Fv2\u002Fmissing-page\"\n  return \u003Cp>Could not find docs page: {params[\"*\"]}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Place the `*` route last among siblings — React Router matches in definition order and will never reach it if an earlier route already matches.\n",{"id":155,"difficulty":50,"q":156,"a":157},"optional-segments","How do you make a route segment optional in React Router v6?","Append `?` to any dynamic segment token to make it **optional**. The param will be `undefined` when the segment is absent.\n\n```jsx\n\u002F\u002F Both \u002Fsearch and \u002Fsearch\u002Fadvanced match this route\n\u003CRoute path=\"\u002Fsearch\u002F:mode?\" element={\u003CSearch \u002F>} \u002F>\n\nfunction Search() {\n  const { mode } = useParams();\n  \u002F\u002F mode is \"advanced\" | undefined\n\n  return (\n    \u003Cdiv>\n      {mode === 'advanced' ? \u003CAdvancedFilters \u002F> : \u003CBasicSearch \u002F>}\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Prefer two explicit routes (`\u002Fsearch` and `\u002Fsearch\u002F:mode`) over an optional segment when the two layouts differ significantly — it keeps each component focused.\n",{"id":159,"difficulty":34,"q":160,"a":161},"nested-routes-outlet","What is Outlet in React Router v6 and why is it needed for nested routes?","**`\u003COutlet \u002F>`** is a placeholder rendered by a parent route component that tells React Router where to inject the matched child route's element. Without it, child routes render nowhere — the URL changes but nothing appears on screen.\n\n```jsx\n\u002F\u002F Route config — Dashboard wraps two child routes\n\u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboardLayout \u002F>}>\n  \u003CRoute index element={\u003COverview \u002F>} \u002F>\n  \u003CRoute path=\"settings\" element={\u003CSettings \u002F>} \u002F>\n\u003C\u002FRoute>\n\n\u002F\u002F DashboardLayout.jsx\nimport { Outlet, NavLink } from 'react-router-dom';\n\nfunction DashboardLayout() {\n  return (\n    \u003Cdiv className=\"dashboard\">\n      \u003Cnav>\n        \u003CNavLink to=\"\u002Fdashboard\">Overview\u003C\u002FNavLink>\n        \u003CNavLink to=\"\u002Fdashboard\u002Fsettings\">Settings\u003C\u002FNavLink>\n      \u003C\u002Fnav>\n      {\u002F* child route renders here *\u002F}\n      \u003Cmain>\u003COutlet \u002F>\u003C\u002Fmain>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Every parent route element that has `\u003CRoute>` children must render `\u003COutlet \u002F>`; forgetting it is the most common nested-route bug.\n",{"id":163,"difficulty":50,"q":164,"a":165},"layout-routes","What is a layout route and how does it differ from a regular route?","A **layout route** is a `\u003CRoute>` that has an `element` for shared UI (nav, sidebar, wrappers) but whose `path` is intentionally omitted or set to the parent prefix. It exists purely to provide structure — it never matches by itself; children match the actual URLs.\n\n```jsx\n\u002F\u002F createBrowserRouter style — no path on the layout route\nconst router = createBrowserRouter([\n  {\n    element: \u003CAppShell \u002F>,   \u002F\u002F nav + footer — no \"path\" key\n    children: [\n      { path: '\u002F',          element: \u003CHome \u002F> },\n      { path: '\u002Fabout',     element: \u003CAbout \u002F> },\n      { path: '\u002Fblog\u002F:slug', element: \u003CBlogPost \u002F> },\n    ],\n  },\n]);\n\n\u002F\u002F AppShell renders Outlet — no URL segment consumed\nfunction AppShell() {\n  return (\n    \u003C>\n      \u003CGlobalNav \u002F>\n      \u003COutlet \u002F>\n      \u003CFooter \u002F>\n    \u003C\u002F>\n  );\n}\n```\n\n**Rule of thumb:** Omit `path` on a layout route entirely; adding `path=\"\"` also works but is less readable.\n",{"id":70,"difficulty":34,"q":167,"a":168},"What is an index route in React Router v6 and when does it render?","An **index route** is a child route with the `index` prop instead of a `path`. It renders inside the parent's `\u003COutlet \u002F>` when the URL matches the parent's path exactly — acting as the default child.\n\n```jsx\n\u003CRoute path=\"\u002Fteam\" element={\u003CTeamLayout \u002F>}>\n  {\u002F* renders at \u002Fteam exactly *\u002F}\n  \u003CRoute index element={\u003CTeamOverview \u002F>} \u002F>\n  {\u002F* renders at \u002Fteam\u002F:memberId *\u002F}\n  \u003CRoute path=\":memberId\" element={\u003CMemberDetail \u002F>} \u002F>\n\u003C\u002FRoute>\n\n\u002F\u002F Without the index route, navigating to \u002Fteam shows\n\u002F\u002F TeamLayout with an empty Outlet — a blank content area.\n```\n\n**Rule of thumb:** Every layout route that owns an `\u003COutlet \u002F>` should have an index route; otherwise the parent path renders a blank content area.\n",{"id":170,"difficulty":50,"q":171,"a":172},"relative-vs-absolute-paths","What is the difference between relative and absolute paths in nested route definitions?","In React Router v6, child `path` values are **relative** by default — they are appended to the parent's path. A **leading `\u002F` makes a path absolute**, bypassing the nesting entirely, which is usually a mistake inside a nested `\u003CRoute>` tree.\n\n```jsx\n\u003CRoute path=\"\u002Fapp\" element={\u003CAppLayout \u002F>}>\n  {\u002F* relative — matches \u002Fapp\u002Fprofile *\u002F}\n  \u003CRoute path=\"profile\" element={\u003CProfile \u002F>} \u002F>\n\n  {\u002F* absolute — matches \u002Fsettings, NOT \u002Fapp\u002Fsettings\n      The parent layout is still rendered because it is\n      an ancestor in the tree, but the URL ignores \u002Fapp *\u002F}\n  \u003CRoute path=\"\u002Fsettings\" element={\u003CSettings \u002F>} \u002F>\n\u003C\u002FRoute>\n\n\u002F\u002F Link usage follows the same rule:\n\u002F\u002F \u003CLink to=\"profile\">   — relative, resolves to \u002Fapp\u002Fprofile\n\u002F\u002F \u003CLink to=\"\u002Fprofile\">  — absolute, resolves to \u002Fprofile\n```\n\n**Rule of thumb:** Never start a nested child `path` with `\u002F`; reserve absolute paths for top-level routes only.\n",{"id":174,"difficulty":50,"q":175,"a":176},"deeply-nested-routes","How do you structure three levels of nesting — an app shell, a section layout, and a detail page?","Each level adds one more `\u003CRoute>` wrapper and one more `\u003COutlet \u002F>` in the corresponding component. React Router renders the entire ancestor chain, threading `\u003COutlet \u002F>` down the tree.\n\n```jsx\nconst router = createBrowserRouter([\n  {\n    \u002F\u002F Level 1 — app shell (nav + footer)\n    element: \u003CAppShell \u002F>,\n    children: [\n      {\n        \u002F\u002F Level 2 — section layout (sidebar)\n        path: 'docs',\n        element: \u003CDocsLayout \u002F>,\n        children: [\n          { index: true, element: \u003CDocsHome \u002F> },\n          {\n            \u002F\u002F Level 3 — detail page\n            path: ':slug',\n            element: \u003CDocPage \u002F>,\n          },\n        ],\n      },\n    ],\n  },\n]);\n\n\u002F\u002F AppShell renders \u003COutlet \u002F> → DocsLayout renders \u003COutlet \u002F> → DocPage\n```\n\n**Rule of thumb:** Keep nesting to three levels maximum; deeper trees are hard to reason about and usually signal that the URL design needs flattening.\n",{"id":178,"difficulty":50,"q":179,"a":180},"outlet-context","How do you pass data from a parent route component to its child routes without prop drilling?","Pass a value to **`\u003COutlet context={...} \u002F>`** in the parent and read it with **`useOutletContext()`** in any descendant. This avoids threading props through multiple intermediate components.\n\n```jsx\n\u002F\u002F Parent layout — fetches the user and passes it down\nfunction DashboardLayout() {\n  const user = useCurrentUser(); \u002F\u002F some hook or loader result\n\n  return (\n    \u003Cdiv>\n      \u003CDashboardNav user={user} \u002F>\n      {\u002F* provide user to all child routes *\u002F}\n      \u003COutlet context={{ user }} \u002F>\n    \u003C\u002Fdiv>\n  );\n}\n\n\u002F\u002F Child route — reads the context\nimport { useOutletContext } from 'react-router-dom';\n\nfunction ProfilePage() {\n  const { user } = useOutletContext();\n  return \u003Ch1>Hello, {user.name}\u003C\u002Fh1>;\n}\n```\n\n**Rule of thumb:** Type the context with a custom hook — `export function useDashboardCtx() { return useOutletContext\u003CDashCtx>(); }` — so TypeScript catches mismatches.\n",{"id":182,"difficulty":50,"q":183,"a":184},"404-inside-nested-layout","How do you show a 404 page that still displays the parent layout's navigation?","Add a **splat child route** (`path=\"*\"`) inside the parent's children. It matches any unrecognised path under the parent and renders inside `\u003COutlet \u002F>`, so the layout's navigation remains visible.\n\n```jsx\n\u003CRoute path=\"\u002Fapp\" element={\u003CAppLayout \u002F>}>\n  \u003CRoute index element={\u003CHome \u002F>} \u002F>\n  \u003CRoute path=\"posts\" element={\u003CPosts \u002F>} \u002F>\n  \u003CRoute path=\"posts\u002F:id\" element={\u003CPostDetail \u002F>} \u002F>\n\n  {\u002F* catches \u002Fapp\u002Fanything-unrecognised *\u002F}\n  \u003CRoute path=\"*\" element={\u003CNotFound \u002F>} \u002F>\n\u003C\u002FRoute>\n\nfunction NotFound() {\n  return (\n    \u003Cdiv>\n      \u003Ch2>404 – Page not found\u003C\u002Fh2>\n      \u003CLink to=\"\u002Fapp\">Back home\u003C\u002FLink>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Add a `path=\"*\"` child to every significant layout; relying on only a root-level catch-all means users lose their navigation context when they land on a 404.\n",{"id":186,"difficulty":50,"q":187,"a":188},"search-params-vs-route-params","What is the difference between route params and URL search params, and which hook reads each?","**Route params** (`:id`) are part of the URL path and identify a resource — they are read with `useParams()`. **Search params** (`?sort=asc&page=2`) are query string key-value pairs used for filtering, sorting, or pagination — they are read and written with `useSearchParams()`.\n\n```jsx\n\u002F\u002F URL: \u002Fproducts\u002F42?color=blue&size=M\n\nfunction ProductDetail() {\n  const { productId } = useParams();       \u002F\u002F \"42\"\n  const [searchParams, setSearchParams] = useSearchParams();\n\n  const color = searchParams.get('color'); \u002F\u002F \"blue\"\n  const size  = searchParams.get('size');  \u002F\u002F \"M\"\n\n  function sortByPrice() {\n    \u002F\u002F updates the query string without a full navigation\n    setSearchParams({ sort: 'price' });\n  }\n\n  return \u003Cp>{productId} — {color} \u002F {size}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Use route params for identity (which resource), search params for state (how to display it) — mixing them leads to bloated URLs and broken back-button behaviour.\n",{"id":190,"difficulty":50,"q":191,"a":192},"create-browser-router-vs-jsx","What are the differences between createBrowserRouter (object config) and the JSX \u003CRoutes>\u002F\u003CRoute> approach?","**`createBrowserRouter`** is the modern Data API introduced in v6.4. It accepts a plain-object route tree and unlocks **loaders**, **actions**, **errorElement**, and **defer** at each route. The older **`\u003CRoutes>\u002F\u003CRoute>` JSX** approach still works but cannot use any Data API features.\n\n```jsx\n\u002F\u002F Object config — enables loaders and actions\nconst router = createBrowserRouter([\n  {\n    path: '\u002Fposts\u002F:id',\n    element: \u003CPostDetail \u002F>,\n    loader: ({ params }) => fetchPost(params.id), \u002F\u002F data API\n    errorElement: \u003CPostError \u002F>,\n  },\n]);\n\u002F\u002F Render: \u003CRouterProvider router={router} \u002F>\n\n\u002F\u002F JSX approach — no loader support\nfunction App() {\n  return (\n    \u003CBrowserRouter>\n      \u003CRoutes>\n        \u003CRoute path=\"\u002Fposts\u002F:id\" element={\u003CPostDetail \u002F>} \u002F>\n      \u003C\u002FRoutes>\n    \u003C\u002FBrowserRouter>\n  );\n}\n```\n\n**Rule of thumb:** Use `createBrowserRouter` for all new projects; it strictly supersedes the JSX approach and is what the React Router team recommends going forward.\n",{"id":194,"difficulty":195,"q":196,"a":197},"loader-pattern","hard","How does a route loader work in React Router v6.4+ and how does the component consume its data?","A **loader** is an async function attached to a route in the object config. React Router calls it before rendering the route, passing `{ params, request }`. The component reads the resolved data via **`useLoaderData()`** — no `useEffect` or loading state needed.\n\n```jsx\n\u002F\u002F Define the loader\nasync function postLoader({ params }) {\n  const res = await fetch(`\u002Fapi\u002Fposts\u002F${params.postId}`);\n  if (!res.ok) throw new Response('Not Found', { status: 404 });\n  return res.json(); \u002F\u002F returned value becomes loader data\n}\n\n\u002F\u002F Attach it to the route\nconst router = createBrowserRouter([\n  {\n    path: '\u002Fposts\u002F:postId',\n    element: \u003CPostDetail \u002F>,\n    loader: postLoader,\n    errorElement: \u003CPostError \u002F>,\n  },\n]);\n\n\u002F\u002F Consume in the component\nimport { useLoaderData } from 'react-router-dom';\n\nfunction PostDetail() {\n  const post = useLoaderData(); \u002F\u002F fully resolved — no loading state\n  return \u003Ch1>{post.title}\u003C\u002Fh1>;\n}\n```\n\n**Rule of thumb:** Throw a `Response` with an appropriate status code from a loader on errors — React Router will render `errorElement` and `useRouteError()` can read the response status.\n",{"id":199,"difficulty":195,"q":200,"a":201},"error-element-nested","How does errorElement bubble in a nested route tree?","When a loader or renderer throws, React Router walks **up the route tree** looking for the nearest ancestor that declares an **`errorElement`**. The first match renders in place of the failed subtree, preserving all ancestor layouts above it.\n\n```jsx\nconst router = createBrowserRouter([\n  {\n    element: \u003CAppShell \u002F>,        \u002F\u002F no errorElement — bubbles further up\n    children: [\n      {\n        path: 'docs',\n        element: \u003CDocsLayout \u002F>,\n        errorElement: \u003CDocsError \u002F>, \u002F\u002F catches errors in any docs child\n        children: [\n          {\n            path: ':slug',\n            element: \u003CDocPage \u002F>,\n            loader: docLoader,     \u002F\u002F throws 404 → DocsError renders\n          },\n        ],\n      },\n    ],\n  },\n]);\n\nfunction DocsError() {\n  const error = useRouteError(); \u002F\u002F the thrown Response or Error\n  return \u003Cp>Docs error: {error.statusText}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Add an `errorElement` at every meaningful layout boundary so a child failure shows a scoped error UI instead of wiping the whole page.\n",{"id":203,"difficulty":34,"q":204,"a":205},"navigate-programmatic","How do you navigate programmatically to a dynamic route, including the param?","Use the **`useNavigate()`** hook. Call the returned function with the full path string (interpolate params yourself) or with a relative path and a `replace` flag when you want to skip history entries.\n\n```jsx\nimport { useNavigate } from 'react-router-dom';\n\nfunction UserList({ users }) {\n  const navigate = useNavigate();\n\n  function handleSelect(userId) {\n    \u002F\u002F absolute path with the param interpolated\n    navigate(`\u002Fusers\u002F${userId}`);\n\n    \u002F\u002F replace current history entry (e.g. after a form submission)\n    \u002F\u002F navigate(`\u002Fusers\u002F${userId}`, { replace: true });\n  }\n\n  return (\n    \u003Cul>\n      {users.map(u => (\n        \u003Cli key={u.id} onClick={() => handleSelect(u.id)}>\n          {u.name}\n        \u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  );\n}\n```\n\n**Rule of thumb:** Prefer `\u003CLink>` over `useNavigate` for user-initiated navigation — it is more accessible and handles middle-click\u002Fopen-in-new-tab correctly.\n",{"id":207,"difficulty":34,"q":208,"a":209},"active-link-nested","How does NavLink determine its active state inside a nested route?","**`\u003CNavLink>`** compares the `to` prop against the current URL. By default it applies the `active` class when the URL **starts with** the `to` value (prefix match). Set `end` to `true` to require an exact match — essential for parent paths like `\u002Fdashboard`.\n\n```jsx\nfunction DocsNav() {\n  return (\n    \u003Cnav>\n      {\u002F* active for \u002Fdocs AND \u002Fdocs\u002Fanything — usually wrong for the root *\u002F}\n      \u003CNavLink to=\"\u002Fdocs\">Docs Home\u003C\u002FNavLink>\n\n      {\u002F* active only at \u002Fdocs exactly *\u002F}\n      \u003CNavLink to=\"\u002Fdocs\" end>Docs Home\u003C\u002FNavLink>\n\n      {\u002F* active for \u002Fdocs\u002Fapi and \u002Fdocs\u002Fapi\u002F... *\u002F}\n      \u003CNavLink to=\"\u002Fdocs\u002Fapi\">API Reference\u003C\u002FNavLink>\n    \u003C\u002Fnav>\n  );\n}\n```\n\n**Rule of thumb:** Always add `end` to `NavLink` items whose `to` value is an ancestor of other nav items; otherwise the parent link stays highlighted on every child page.\n",{"id":211,"difficulty":195,"q":212,"a":213},"route-param-validation","How would you validate a route param (e.g. ensure :id is numeric) and redirect if it is invalid?","The cleanest place is inside a **loader** — parse and validate the param, then throw a redirect `Response` if it fails. The component never renders with invalid data.\n\n```jsx\nimport { redirect } from 'react-router-dom';\n\nasync function postLoader({ params }) {\n  const id = Number(params.postId);\n\n  \u002F\u002F Redirect to 404 layout if id is not a valid integer\n  if (!Number.isInteger(id) || id \u003C= 0) {\n    throw redirect('\u002F404');           \u002F\u002F or throw new Response('', { status: 404 })\n  }\n\n  const res = await fetch(`\u002Fapi\u002Fposts\u002F${id}`);\n  if (!res.ok) throw new Response('Post not found', { status: 404 });\n  return res.json();\n}\n\nconst router = createBrowserRouter([\n  {\n    path: '\u002Fposts\u002F:postId',\n    loader: postLoader,\n    element: \u003CPostDetail \u002F>,\n    errorElement: \u003CPostError \u002F>,\n  },\n]);\n```\n\n**Rule of thumb:** Validate params in loaders, not in render; by the time the component runs the data should already be known-good.\n",{"id":215,"difficulty":195,"q":216,"a":217},"outlet-context-typescript","How do you type useOutletContext in TypeScript to avoid using any?","Create a **typed wrapper hook** that calls `useOutletContext` with the correct generic. Export it from the layout file so child routes import the typed version, not the raw hook.\n\n```tsx\n\u002F\u002F DashboardLayout.tsx\nimport { Outlet, useOutletContext } from 'react-router-dom';\n\ninterface DashboardCtx {\n  user: { id: number; name: string; role: string };\n  refetch: () => void;\n}\n\n\u002F\u002F Typed wrapper — export this, not useOutletContext directly\nexport function useDashboardCtx() {\n  return useOutletContext\u003CDashboardCtx>();\n}\n\nexport function DashboardLayout() {\n  const user = useCurrentUser();\n  const refetch = useRefetch();\n\n  return (\n    \u003Cdiv>\n      \u003CDashboardNav \u002F>\n      \u003COutlet context={{ user, refetch } satisfies DashboardCtx} \u002F>\n    \u003C\u002Fdiv>\n  );\n}\n\n\u002F\u002F ProfilePage.tsx — fully typed, no any\nimport { useDashboardCtx } from '.\u002FDashboardLayout';\n\nfunction ProfilePage() {\n  const { user, refetch } = useDashboardCtx(); \u002F\u002F typed!\n  return \u003Ch1>{user.name}\u003C\u002Fh1>;\n}\n```\n\n**Rule of thumb:** Co-locate the typed hook with the layout component that provides the context — it acts as the single source of truth for the context shape.\n",{"description":32},"React Router v6 dynamic and nested routes interview questions — useParams, Outlet, layout routes, index routes, useOutletContext, splat routes, and data loaders.","react\u002Frouting\u002Fdynamic-nested-routes","Dynamic and Nested Routes","JPlp09gxgi2ITBy45JiCXT1aq106IlC4O1y4c0dPcbM",{"id":224,"title":225,"body":226,"description":32,"difficulty":50,"extension":35,"framework":10,"frameworkSlug":8,"meta":230,"navigation":37,"order":231,"path":232,"questions":233,"questionsCount":121,"related":122,"seo":313,"seoDescription":314,"stem":315,"subtopic":225,"topic":19,"topicSlug":21,"updated":126,"__hash__":316},"qa\u002Freact\u002Frouting\u002Fnavigation-hooks.md","Navigation Hooks",{"type":29,"value":227,"toc":228},[],{"title":32,"searchDepth":11,"depth":11,"links":229},[],{},3,"\u002Freact\u002Frouting\u002Fnavigation-hooks",[234,238,241,245,249,253,257,261,265,269,273,277,281,285,289,293,297,301,305,309],{"id":235,"difficulty":34,"q":236,"a":237},"use-navigate-basic","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":110,"difficulty":34,"q":239,"a":240},"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":242,"difficulty":34,"q":243,"a":244},"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":246,"difficulty":50,"q":247,"a":248},"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":250,"difficulty":34,"q":251,"a":252},"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":254,"difficulty":34,"q":255,"a":256},"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":258,"difficulty":50,"q":259,"a":260},"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":262,"difficulty":50,"q":263,"a":264},"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":266,"difficulty":50,"q":267,"a":268},"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":270,"difficulty":50,"q":271,"a":272},"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":274,"difficulty":195,"q":275,"a":276},"use-routes-dynamic-config","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":278,"difficulty":195,"q":279,"a":280},"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":282,"difficulty":50,"q":283,"a":284},"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":286,"difficulty":195,"q":287,"a":288},"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":290,"difficulty":50,"q":291,"a":292},"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":294,"difficulty":50,"q":295,"a":296},"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":298,"difficulty":50,"q":299,"a":300},"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":302,"difficulty":195,"q":303,"a":304},"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":306,"difficulty":50,"q":307,"a":308},"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":310,"difficulty":50,"q":311,"a":312},"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",{"description":32},"React Router v6 navigation hooks interview questions — useNavigate, useParams, useLocation, useSearchParams, useMatch, useBlocker, and programmatic navigation patterns.","react\u002Frouting\u002Fnavigation-hooks","abQ4KFyCLOvgdO16RQn_HXrPwd96ieDJPJmP7kOkrlQ",{"id":318,"title":319,"body":320,"description":32,"difficulty":50,"extension":35,"framework":10,"frameworkSlug":8,"meta":324,"navigation":37,"order":325,"path":326,"questions":327,"questionsCount":121,"related":122,"seo":407,"seoDescription":408,"stem":409,"subtopic":319,"topic":19,"topicSlug":21,"updated":126,"__hash__":410},"qa\u002Freact\u002Frouting\u002Fprotected-routes.md","Protected Routes",{"type":29,"value":321,"toc":322},[],{"title":32,"searchDepth":11,"depth":11,"links":323},[],{},4,"\u002Freact\u002Frouting\u002Fprotected-routes",[328,332,336,340,344,348,352,356,360,364,368,372,376,380,383,387,391,395,399,403],{"id":329,"difficulty":34,"q":330,"a":331},"what-is-protected-route","What is a protected route and why is it needed in a React SPA?","A **protected route** is a route that only renders its component when the user meets\na certain condition — typically being authenticated. Without it, any visitor who knows\na URL can directly navigate to sensitive pages (dashboards, admin panels, account\nsettings) because the browser fetches the static JS bundle and renders the page\nentirely client-side.\n\nThe solution is an **auth guard** component that checks the auth state before\nrendering child routes. If the check fails it **redirects** to the login page instead\nof rendering the protected content.\n\n```jsx\n\u002F\u002F Minimal guard — renders children or redirects\nfunction RequireAuth({ children }) {\n  const { user } = useAuth(); \u002F\u002F check auth state\n  if (!user) {\n    return \u003CNavigate to=\"\u002Flogin\" replace \u002F>; \u002F\u002F kick to login\n  }\n  return children; \u002F\u002F authenticated — render the page\n}\n```\n\n**Rule of thumb:** A protected route is just a conditional render — show the page or\nredirect; the *real* security still lives on the server.\n",{"id":333,"difficulty":34,"q":334,"a":335},"require-auth-wrapper","How do you implement a RequireAuth wrapper component using React Router v6's Navigate?","In React Router v6 the `\u003CNavigate>` component performs a **declarative redirect**.\nA `RequireAuth` wrapper checks auth state and either renders `\u003COutlet \u002F>` (for\nlayout-route usage) or `children` (for direct wrapping).\n\n```jsx\nimport { Navigate, Outlet, useLocation } from 'react-router-dom';\nimport { useAuth } from '..\u002Fcontext\u002FAuthContext';\n\n\u002F\u002F Layout-route variant — works with nested \u003CRoute> trees\nexport function RequireAuth() {\n  const { user } = useAuth();\n  const location = useLocation(); \u002F\u002F capture current path\n\n  if (!user) {\n    \u002F\u002F Pass current location so login can redirect back after success\n    return \u003CNavigate to=\"\u002Flogin\" state={{ from: location }} replace \u002F>;\n  }\n\n  return \u003COutlet \u002F>; \u002F\u002F render matched child route\n}\n```\n\nIn the router config, nest protected routes under `RequireAuth`:\n\n```jsx\n\u003CRoute element={\u003CRequireAuth \u002F>}>\n  \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n  \u003CRoute path=\"\u002Fsettings\" element={\u003CSettings \u002F>} \u002F>\n\u003C\u002FRoute>\n```\n\n**Rule of thumb:** Use the **layout-route** (`\u003COutlet \u002F>`) pattern rather than\nwrapping every `\u003CRoute element>` individually — one guard covers all children.\n",{"id":337,"difficulty":50,"q":338,"a":339},"preserve-intended-destination","How do you preserve the intended destination so users land on the right page after login?","When unauthenticated users hit a protected URL you pass the current **location** in\n`state` on the redirect. After a successful login you read `location.state.from` and\ncall `navigate()` to send them there instead of a hard-coded fallback.\n\n```jsx\n\u002F\u002F 1. Guard passes current location in redirect state\nfunction RequireAuth() {\n  const { user } = useAuth();\n  const location = useLocation();\n  if (!user) {\n    return \u003CNavigate to=\"\u002Flogin\" state={{ from: location }} replace \u002F>;\n  }\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F 2. Login page reads state.from after successful login\nfunction LoginPage() {\n  const { login } = useAuth();\n  const navigate = useNavigate();\n  const location = useLocation();\n  const from = location.state?.from?.pathname ?? '\u002Fdashboard'; \u002F\u002F fallback\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    await login(formData); \u002F\u002F authenticate\n    navigate(from, { replace: true }); \u002F\u002F go to original destination\n  }\n  \u002F\u002F ...render form\n}\n```\n\n**Rule of thumb:** Always supply a **fallback path** (`?? '\u002Fdashboard'`) — if the\nuser bookmarks `\u002Flogin` directly, `state` is undefined.\n",{"id":341,"difficulty":50,"q":342,"a":343},"rbac-route-protection","How do you implement role-based access control (RBAC) in React Router v6 routes?","Extend the `RequireAuth` pattern with an `allowedRoles` prop. The guard checks both\n**authentication** and **authorization** before rendering.\n\n```jsx\nimport { Navigate, Outlet, useLocation } from 'react-router-dom';\nimport { useAuth } from '..\u002Fcontext\u002FAuthContext';\n\n\u002F\u002F allowedRoles: string[] — e.g. ['admin', 'editor']\nfunction RequireRole({ allowedRoles }) {\n  const { user } = useAuth();\n  const location = useLocation();\n\n  if (!user) {\n    \u002F\u002F Not logged in → redirect to login\n    return \u003CNavigate to=\"\u002Flogin\" state={{ from: location }} replace \u002F>;\n  }\n\n  if (!allowedRoles.includes(user.role)) {\n    \u002F\u002F Logged in but wrong role → show 403\n    return \u003CNavigate to=\"\u002F403\" replace \u002F>;\n  }\n\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F Router config\n\u003CRoute element={\u003CRequireRole allowedRoles={['admin']} \u002F>}>\n  \u003CRoute path=\"\u002Fadmin\" element={\u003CAdminPanel \u002F>} \u002F>\n\u003C\u002FRoute>\n```\n\n**Rule of thumb:** Always separate the two checks — unauthenticated users should\nsee the **login page**, not a 403; authorized users with the wrong role should see\n**403**, not the login page.\n",{"id":345,"difficulty":50,"q":346,"a":347},"protecting-nested-routes","How do you protect an entire branch of nested routes with a single layout route guard?","React Router v6's **layout route** pattern (a `\u003CRoute>` with no `path` but with an\n`element`) lets you wrap a whole subtree. Combine this with `RequireAuth` to cover\nall descendants at once.\n\n```jsx\n\u003CRoutes>\n  {\u002F* Public routes *\u002F}\n  \u003CRoute path=\"\u002F\" element={\u003CHome \u002F>} \u002F>\n  \u003CRoute path=\"\u002Flogin\" element={\u003CLoginPage \u002F>} \u002F>\n\n  {\u002F* Protected subtree — single guard for all children *\u002F}\n  \u003CRoute element={\u003CRequireAuth \u002F>}>\n    \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n    \u003CRoute path=\"\u002Fprofile\" element={\u003CProfile \u002F>} \u002F>\n\n    {\u002F* Doubly-nested admin section with role check *\u002F}\n    \u003CRoute element={\u003CRequireRole allowedRoles={['admin']} \u002F>}>\n      \u003CRoute path=\"\u002Fadmin\" element={\u003CAdminPanel \u002F>} \u002F>\n      \u003CRoute path=\"\u002Fadmin\u002Fusers\" element={\u003CUserManager \u002F>} \u002F>\n    \u003C\u002FRoute>\n  \u003C\u002FRoute>\n\u003C\u002FRoutes>\n```\n\n**Rule of thumb:** Think of layout routes as **middleware layers** — stack them to\ncompose auth + role checks without repeating logic on each leaf route.\n",{"id":349,"difficulty":50,"q":350,"a":351},"lazy-loading-protected-routes","How do you lazy-load protected route components with React.lazy and Suspense?","Use `React.lazy` to **code-split** heavy protected pages so their JS chunk is only\ndownloaded after the guard confirms auth. Wrap the lazy import in `\u003CSuspense>` inside\n(or outside) the `RequireAuth` element.\n\n```jsx\nimport React, { Suspense } from 'react';\nimport { Route, Routes } from 'react-router-dom';\nimport { RequireAuth } from '.\u002Fguards\u002FRequireAuth';\n\n\u002F\u002F Lazy imports — bundle splits here\nconst Dashboard = React.lazy(() => import('.\u002Fpages\u002FDashboard'));\nconst AdminPanel = React.lazy(() => import('.\u002Fpages\u002FAdminPanel'));\n\nfunction AppRoutes() {\n  return (\n    \u002F\u002F Suspense can live here to cover all lazy routes at once\n    \u003CSuspense fallback={\u003Cdiv>Loading…\u003C\u002Fdiv>}>\n      \u003CRoutes>\n        \u003CRoute element={\u003CRequireAuth \u002F>}>\n          \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n          \u003CRoute path=\"\u002Fadmin\" element={\u003CAdminPanel \u002F>} \u002F>\n        \u003C\u002FRoute>\n      \u003C\u002FRoutes>\n    \u003C\u002FSuspense>\n  );\n}\n```\n\n**Rule of thumb:** Place `\u003CSuspense>` **outside** `\u003CRoutes>` to handle all lazy\nroutes uniformly; only drop it inside a specific `\u003CRoute>` if you need per-page\nloading UI.\n",{"id":353,"difficulty":195,"q":354,"a":355},"token-storage-security","Compare localStorage, cookies, and in-memory storage for auth tokens and their implications for routing guards.","The storage choice affects how the auth guard reads the token on each render and\nwhat attack surface you expose.\n\n| Storage | XSS risk | CSRF risk | Survives refresh | Notes |\n|---|---|---|---|---|\n| `localStorage` | High — JS-readable | None | Yes | Avoid for sensitive tokens |\n| `httpOnly` cookie | None — server-set | Medium | Yes | Best for session tokens |\n| Memory (React state) | Low | None | No | Requires silent-refresh |\n\n```jsx\n\u002F\u002F In-memory pattern — token lives only in AuthContext state\nconst AuthContext = createContext(null);\n\nexport function AuthProvider({ children }) {\n  const [user, setUser] = useState(null); \u002F\u002F cleared on tab close\n\n  \u002F\u002F On mount, attempt a silent refresh via httpOnly refresh-token cookie\n  useEffect(() => {\n    api.post('\u002Fauth\u002Frefresh')\n      .then(({ data }) => setUser(data.user)) \u002F\u002F restore session\n      .catch(() => setUser(null)); \u002F\u002F no valid cookie → stay logged out\n  }, []);\n\n  return \u003CAuthContext.Provider value={{ user, setUser }}>{children}\u003C\u002FAuthContext.Provider>;\n}\n```\n\n**Rule of thumb:** Store the **access token in memory**, set the **refresh token as\nan httpOnly cookie** — you get XSS-safety for the short-lived token and automatic\nsession persistence without `localStorage`.\n",{"id":357,"difficulty":50,"q":358,"a":359},"optimistic-auth-loading-state","What is the difference between optimistic auth and a loading-state guard, and which should you use?","On initial page load the auth context hasn't yet rehydrated (e.g., the silent-refresh\ncall is in-flight). Two strategies exist:\n\n**Optimistic auth** assumes the user *is* logged in until proven otherwise — renders\nthe protected page immediately, then potentially redirects. Can cause a **flash** of\nprotected content.\n\n**Loading-state guard** holds rendering until auth is confirmed. Prevents flashing\nbut shows a spinner on every cold load.\n\n```jsx\nfunction RequireAuth() {\n  const { user, loading } = useAuth();\n  const location = useLocation();\n\n  if (loading) {\n    \u002F\u002F Wait for silent-refresh before deciding\n    return \u003Cdiv className=\"spinner\" aria-label=\"Checking auth…\" \u002F>;\n  }\n\n  if (!user) {\n    return \u003CNavigate to=\"\u002Flogin\" state={{ from: location }} replace \u002F>;\n  }\n\n  return \u003COutlet \u002F>;\n}\n```\n\n**Rule of thumb:** Always use the **loading-state guard** — a brief spinner is a\nfar better UX than flashing private data at unauthenticated users.\n",{"id":361,"difficulty":195,"q":362,"a":363},"token-expiry-interceptor","How do you handle token expiry mid-session without forcing the user to re-login?","Set up an **Axios\u002FFetch interceptor** that detects a 401 response, silently requests\na new access token using the refresh-token cookie, retries the original request, and\nonly redirects to login when the refresh itself fails.\n\n```jsx\n\u002F\u002F api.js — Axios instance with interceptor\nimport axios from 'axios';\n\nconst api = axios.create({ baseURL: '\u002Fapi', withCredentials: true });\n\nlet refreshPromise = null; \u002F\u002F prevent concurrent refresh calls\n\napi.interceptors.response.use(\n  res => res,\n  async error => {\n    const original = error.config;\n    if (error.response?.status === 401 && !original._retry) {\n      original._retry = true;\n      \u002F\u002F Deduplicate: reuse an in-flight refresh\n      refreshPromise = refreshPromise ?? api.post('\u002Fauth\u002Frefresh').finally(() => {\n        refreshPromise = null;\n      });\n      await refreshPromise;        \u002F\u002F wait for new token\n      return api(original);        \u002F\u002F retry original request\n    }\n    \u002F\u002F Refresh failed → redirect to login\n    window.location.href = '\u002Flogin';\n    return Promise.reject(error);\n  }\n);\n```\n\n**Rule of thumb:** Use a **single shared refresh promise** to prevent multiple\nparallel 401 responses from each triggering their own refresh race.\n",{"id":365,"difficulty":50,"q":366,"a":367},"auth-context-pattern","Describe the AuthContext + useAuth hook pattern and why it's preferred for protecting routes.","**AuthContext** stores auth state (user, loading, login, logout) in React context so\nany component in the tree — including route guards — can read it without prop-drilling.\nThe `useAuth` hook wraps `useContext` and throws early if used outside the provider.\n\n```jsx\n\u002F\u002F context\u002FAuthContext.jsx\nimport { createContext, useContext, useState, useEffect } from 'react';\n\nconst AuthContext = createContext(null);\n\nexport function AuthProvider({ children }) {\n  const [user, setUser] = useState(null);\n  const [loading, setLoading] = useState(true);\n\n  useEffect(() => {\n    \u002F\u002F Restore session on mount\n    api.get('\u002Fauth\u002Fme')\n      .then(({ data }) => setUser(data))\n      .catch(() => setUser(null))\n      .finally(() => setLoading(false)); \u002F\u002F stop spinner\n  }, []);\n\n  const login = async (creds) => {\n    const { data } = await api.post('\u002Fauth\u002Flogin', creds);\n    setUser(data.user); \u002F\u002F update global state\n  };\n\n  const logout = async () => {\n    await api.post('\u002Fauth\u002Flogout');\n    setUser(null);\n  };\n\n  return (\n    \u003CAuthContext.Provider value={{ user, loading, login, logout }}>\n      {children}\n    \u003C\u002FAuthContext.Provider>\n  );\n}\n\n\u002F\u002F Enforces provider presence\nexport function useAuth() {\n  const ctx = useContext(AuthContext);\n  if (!ctx) throw new Error('useAuth must be used inside AuthProvider');\n  return ctx;\n}\n```\n\n**Rule of thumb:** Throw in `useAuth` when the context is null — it turns a silent\nwrong-render into an obvious developer error.\n",{"id":369,"difficulty":50,"q":370,"a":371},"route-level-vs-component-level","When should you use route-level guards versus component-level access checks?","**Route-level guards** (`RequireAuth` layout routes) handle coarse-grained access —\nthey prevent entire pages from rendering. **Component-level guards** handle\nfine-grained UI elements within a page (e.g., an \"Edit\" button visible only to\nadmins).\n\n```jsx\n\u002F\u002F Route-level guard — whole page blocked for non-admins\n\u003CRoute element={\u003CRequireRole allowedRoles={['admin']} \u002F>}>\n  \u003CRoute path=\"\u002Fadmin\" element={\u003CAdminDashboard \u002F>} \u002F>\n\u003C\u002FRoute>\n\n\u002F\u002F Component-level guard — same page, conditional UI\nfunction ArticlePage() {\n  const { user } = useAuth();\n  return (\n    \u003Carticle>\n      \u003Ch1>{article.title}\u003C\u002Fh1>\n      \u003Cp>{article.body}\u003C\u002Fp>\n      {\u002F* Only editors see the Edit button *\u002F}\n      {user?.role === 'editor' && (\n        \u003Cbutton onClick={handleEdit}>Edit\u003C\u002Fbutton>\n      )}\n    \u003C\u002Farticle>\n  );\n}\n```\n\n**Rule of thumb:** Use **route-level** guards for page access, **component-level**\nguards for UI elements — never rely on hiding UI elements as the sole security\nmeasure.\n",{"id":373,"difficulty":50,"q":374,"a":375},"testing-protected-routes","How do you test protected routes by mocking the auth context?","Render a custom wrapper that supplies a mock `AuthContext` value. This lets you test\nboth the authenticated and unauthenticated branches without a real auth server.\n\n```jsx\n\u002F\u002F test\u002Futils.jsx\nimport { render } from '@testing-library\u002Freact';\nimport { MemoryRouter } from 'react-router-dom';\nimport { AuthContext } from '..\u002Fcontext\u002FAuthContext';\n\nexport function renderWithAuth(ui, { user = null, route = '\u002F' } = {}) {\n  return render(\n    \u002F\u002F MemoryRouter lets us set the initial URL\n    \u003CMemoryRouter initialEntries={[route]}>\n      \u003CAuthContext.Provider value={{ user, loading: false }}>\n        {ui}\n      \u003C\u002FAuthContext.Provider>\n    \u003C\u002FMemoryRouter>\n  );\n}\n\n\u002F\u002F dashboard.test.jsx\nimport { screen } from '@testing-library\u002Freact';\nimport { Routes, Route } from 'react-router-dom';\nimport { RequireAuth } from '..\u002Fguards\u002FRequireAuth';\nimport { Dashboard } from '..\u002Fpages\u002FDashboard';\n\ntest('redirects to login when unauthenticated', () => {\n  renderWithAuth(\n    \u003CRoutes>\n      \u003CRoute path=\"\u002Flogin\" element={\u003Cp>Login page\u003C\u002Fp>} \u002F>\n      \u003CRoute element={\u003CRequireAuth \u002F>}>\n        \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n      \u003C\u002FRoute>\n    \u003C\u002FRoutes>,\n    { user: null, route: '\u002Fdashboard' } \u002F\u002F no user\n  );\n  expect(screen.getByText('Login page')).toBeInTheDocument();\n});\n```\n\n**Rule of thumb:** Use `MemoryRouter` (not `BrowserRouter`) in tests — you control\nthe initial URL without touching `window.location`.\n",{"id":377,"difficulty":195,"q":378,"a":379},"server-vs-client-protection","What are the trade-offs between client-side route protection and server-side protection?","**Client-side protection** (React Router guards) is UI-only — it prevents rendering\nthe component but the API endpoints are still reachable by anyone with network tools.\n**Server-side protection** validates the token on every API call and is the true\nsecurity boundary.\n\n```jsx\n\u002F\u002F Client-side guard — UX only, not a security boundary\nfunction RequireAuth() {\n  const { user } = useAuth();\n  if (!user) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>;\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F Server-side guard (Express example) — real security\nfunction authMiddleware(req, res, next) {\n  const token = req.cookies.accessToken;\n  try {\n    req.user = jwt.verify(token, process.env.JWT_SECRET); \u002F\u002F throws if invalid\n    next();\n  } catch {\n    res.status(401).json({ error: 'Unauthorized' }); \u002F\u002F API rejects bad token\n  }\n}\n\n\u002F\u002F Every protected API route uses the middleware\nrouter.get('\u002Fapi\u002Fdashboard-data', authMiddleware, getDashboardData);\n```\n\n**Rule of thumb:** Client-side guards protect **UX**; server-side guards protect\n**data** — you need both, and the server is the only one that matters for security.\n",{"id":110,"difficulty":34,"q":381,"a":382},"Why should you use replace={true} on the Navigate redirect in an auth guard?","Without `replace`, the redirect adds the protected URL to the **browser history\nstack**. After login the user presses Back, lands on the protected page URL in\nhistory, gets redirected again, and is stuck in a loop. Using `replace` **overwrites**\nthe history entry so the Back button goes to wherever they came from before the\nprotected URL.\n\n```jsx\nfunction RequireAuth() {\n  const { user } = useAuth();\n  const location = useLocation();\n\n  if (!user) {\n    return (\n      \u003CNavigate\n        to=\"\u002Flogin\"\n        state={{ from: location }}\n        replace   \u002F\u002F ← replaces instead of pushing — prevents back-button loop\n      \u002F>\n    );\n  }\n  return \u003COutlet \u002F>;\n}\n```\n\n**Rule of thumb:** Always pass `replace` on auth redirects to avoid **back-button\nloops** — it's a one-word fix with a significant UX impact.\n",{"id":384,"difficulty":34,"q":385,"a":386},"public-only-routes","How do you create a \"public-only\" route that redirects authenticated users away from the login page?","The inverse of `RequireAuth` — a **GuestOnly** guard redirects authenticated users\nwho try to visit `\u002Flogin` or `\u002Fregister` to the dashboard instead of showing them\na form they don't need.\n\n```jsx\nfunction GuestOnly() {\n  const { user, loading } = useAuth();\n  const location = useLocation();\n\n  if (loading) return \u003Cdiv>Loading…\u003C\u002Fdiv>;\n\n  if (user) {\n    \u002F\u002F Authenticated user tried to reach \u002Flogin — send them home\n    const destination = location.state?.from?.pathname ?? '\u002Fdashboard';\n    return \u003CNavigate to={destination} replace \u002F>;\n  }\n\n  return \u003COutlet \u002F>; \u002F\u002F not logged in — show login\u002Fregister\n}\n\n\u002F\u002F Router config\n\u003CRoute element={\u003CGuestOnly \u002F>}>\n  \u003CRoute path=\"\u002Flogin\" element={\u003CLoginPage \u002F>} \u002F>\n  \u003CRoute path=\"\u002Fregister\" element={\u003CRegisterPage \u002F>} \u002F>\n\u003C\u002FRoute>\n```\n\n**Rule of thumb:** Pair every `RequireAuth` guard with a `GuestOnly` guard — without\nit, logged-in users see the login form and get confused.\n",{"id":388,"difficulty":50,"q":389,"a":390},"persisting-auth-across-refresh","How do you persist authentication state across a full page refresh in a React SPA?","On a hard refresh React state is wiped. To restore auth you have two options:\n(1) read a token from `localStorage` synchronously on mount, or (2) fire a\n**silent-refresh** API call on mount using an httpOnly refresh-token cookie.\nOption 2 is more secure.\n\n```jsx\nexport function AuthProvider({ children }) {\n  const [user, setUser] = useState(null);\n  const [loading, setLoading] = useState(true); \u002F\u002F true until we know\n\n  useEffect(() => {\n    \u002F\u002F Silent refresh — relies on httpOnly cookie sent automatically\n    api.post('\u002Fauth\u002Frefresh')\n      .then(({ data }) => {\n        setUser(data.user);      \u002F\u002F restore user object\n      })\n      .catch(() => {\n        setUser(null);           \u002F\u002F no valid cookie → stay logged out\n      })\n      .finally(() => {\n        setLoading(false);       \u002F\u002F guards can now make a decision\n      });\n  }, []); \u002F\u002F run once on mount\n\n  return (\n    \u003CAuthContext.Provider value={{ user, loading }}>\n      {children}\n    \u003C\u002FAuthContext.Provider>\n  );\n}\n```\n\n**Rule of thumb:** Keep `loading: true` until the refresh attempt resolves — every\nguard in the app blocks on this flag, preventing a premature redirect to `\u002Flogin`.\n",{"id":392,"difficulty":195,"q":393,"a":394},"multiple-guards-composition","How do you compose multiple guards (auth + subscription + feature flag) on a single route?","Nest layout routes — each wraps the next, and all must pass before the leaf renders.\nThis keeps each guard **single-responsibility** and independently testable.\n\n```jsx\n\u002F\u002F Each guard does one job and delegates via \u003COutlet \u002F>\nfunction RequireSubscription() {\n  const { user } = useAuth();\n  if (!user?.subscription?.active) {\n    return \u003CNavigate to=\"\u002Fupgrade\" replace \u002F>;\n  }\n  return \u003COutlet \u002F>;\n}\n\nfunction RequireFeatureFlag({ flag }) {\n  const { flags } = useFeatureFlags();\n  if (!flags[flag]) {\n    return \u003CNavigate to=\"\u002Fcoming-soon\" replace \u002F>;\n  }\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F Router — guards stack innermost-last\n\u003CRoute element={\u003CRequireAuth \u002F>}>             {\u002F* 1st: auth *\u002F}\n  \u003CRoute element={\u003CRequireSubscription \u002F>}>   {\u002F* 2nd: paid plan *\u002F}\n    \u003CRoute element={\u003CRequireFeatureFlag flag=\"ai-tools\" \u002F>}> {\u002F* 3rd: flag *\u002F}\n      \u003CRoute path=\"\u002Fai-tools\" element={\u003CAiTools \u002F>} \u002F>\n    \u003C\u002FRoute>\n  \u003C\u002FRoute>\n\u003C\u002FRoute>\n```\n\n**Rule of thumb:** Compose guards by **nesting layout routes** rather than combining\nlogic in one mega-guard — each layer stays readable and individually unit-testable.\n",{"id":396,"difficulty":50,"q":397,"a":398},"redirect-loop-debugging","What causes an infinite redirect loop in protected routes and how do you fix it?","A redirect loop happens when the guard sends the user to `\u002Flogin`, but `\u002Flogin` is\nalso behind the guard (or the auth state never resolves to `true`), so the guard\nredirects again immediately.\n\nCommon causes and fixes:\n\n```jsx\n\u002F\u002F BUG: \u002Flogin is inside RequireAuth — redirects loop forever\n\u003CRoute element={\u003CRequireAuth \u002F>}>\n  \u003CRoute path=\"\u002Flogin\" element={\u003CLoginPage \u002F>} \u002F> {\u002F* ← wrong! *\u002F}\n  \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n\u003C\u002FRoute>\n\n\u002F\u002F FIX: \u002Flogin must be outside the guard\n\u003CRoutes>\n  \u003CRoute path=\"\u002Flogin\" element={\u003CLoginPage \u002F>} \u002F>  {\u002F* public *\u002F}\n\n  \u003CRoute element={\u003CRequireAuth \u002F>}>\n    \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F> {\u002F* protected *\u002F}\n  \u003C\u002FRoute>\n\u003C\u002FRoutes>\n\n\u002F\u002F BUG 2: guard doesn't wait for loading — user is null on first render\n\u002F\u002F even when a valid refresh cookie exists → immediate redirect to login\nfunction RequireAuth() {\n  const { user, loading } = useAuth();\n  if (loading) return null; \u002F\u002F ← wait before deciding\n  if (!user) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>;\n  return \u003COutlet \u002F>;\n}\n```\n\n**Rule of thumb:** If you see a redirect loop, check two things: is the redirect\n**target outside the guard**, and is the guard **waiting for the loading flag**?\n",{"id":400,"difficulty":34,"q":401,"a":402},"useNavigate-vs-navigate-component","When should you use the Navigate component versus the useNavigate hook in auth flows?","`\u003CNavigate>` is a **declarative** redirect that happens during render — ideal for\nguards that decide synchronously (user is or isn't logged in right now).\n`useNavigate()` returns an **imperative** `navigate()` function — ideal for\nredirecting *after* an async operation (login form submit, token refresh).\n\n```jsx\n\u002F\u002F Declarative — fires during render, perfect for guards\nfunction RequireAuth() {\n  const { user } = useAuth();\n  if (!user) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>; \u002F\u002F renders a redirect\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F Imperative — fires after async logic, perfect for post-login redirect\nfunction LoginPage() {\n  const { login } = useAuth();\n  const navigate = useNavigate();\n  const location = useLocation();\n  const from = location.state?.from?.pathname ?? '\u002Fdashboard';\n\n  async function handleSubmit(credentials) {\n    await login(credentials);       \u002F\u002F async: wait for server response\n    navigate(from, { replace: true }); \u002F\u002F then redirect\n  }\n}\n```\n\n**Rule of thumb:** Use `\u003CNavigate>` in **guards** (render-time decision), use\n`useNavigate` in **event handlers** (post-async decision).\n",{"id":404,"difficulty":50,"q":405,"a":406},"404-vs-403-handling","How should a protected-route guard distinguish between a 404 (route not found) and a 403 (forbidden)?","A **404** means the URL doesn't match any route. A **403** means the URL matched but\nthe user lacks permission. Conflating them leaks information (you reveal the route\nexists to unauthorized users). In practice, both are acceptable — many apps return\na 404 for unauthorized routes to avoid enumeration.\n\n```jsx\nfunction RequireRole({ allowedRoles }) {\n  const { user } = useAuth();\n  const location = useLocation();\n\n  if (!user) {\n    \u002F\u002F Not logged in → login (don't reveal the page exists)\n    return \u003CNavigate to=\"\u002Flogin\" state={{ from: location }} replace \u002F>;\n  }\n\n  if (!allowedRoles.includes(user.role)) {\n    \u002F\u002F Logged in, wrong role — choose 403 or 404 based on policy\n    return \u003CNavigate to=\"\u002F403\" replace \u002F>; \u002F\u002F explicit \"forbidden\" page\n    \u002F\u002F OR: return \u003CNavigate to=\"\u002F404\" replace \u002F>; \u002F\u002F stealth — hides route existence\n  }\n\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F Catch-all 404 must be the last route in the tree\n\u003CRoute path=\"*\" element={\u003CNotFoundPage \u002F>} \u002F>\n```\n\n**Rule of thumb:** Send unauthenticated users to **login**, send authenticated but\nunauthorized users to **403** (or 404 for sensitive admin routes) — keep the\ndistinction deliberate and documented.\n",{"description":32},"React Router v6 protected routes interview questions — RequireAuth wrapper, Navigate redirect, role-based access, auth context, token storage, and redirect-after-login patterns.","react\u002Frouting\u002Fprotected-routes","WBfNu4OmQJrFDE-JcwSQzTUWre5iGF2lnnAP77GmAOE",1782244096663]