Irbisa · Cheatsheet

Routing

React Router v7, nested routes and layouts, params and search params, loaders and actions, protected routes, code splitting by route

Middle Developer · 21 questions

  1. 01

    What is client-side routing, and how does it differ from traditional server navigation?

    With client-side routing the app intercepts link clicks, changes the URL through the History API (history.pushState) and renders the matching components without a full page reload.

  2. 02

    How do you set up routing in React Router v7, and what are its three modes?

    React Router v7 runs in declarative mode (<BrowserRouter> with <Routes>), data mode (createBrowserRouter plus RouterProvider, which adds loaders, actions and pending states) or framework mode (the former Remix: a Vite plugin, file-based routes, SSR and generated types).

    import { createBrowserRouter, RouterProvider } from 'react-router';import { createRoot } from 'react-dom/client'; const router = createBrowserRouter([  {    path: '/',…
  3. 03

    What is the difference between <Link>, <NavLink> and useNavigate, and why not use a plain <a href>?

    <Link> renders an <a> that navigates on the client, <NavLink> is a <Link> that knows whether it is active (for menus and tabs), and useNavigate navigates from code, for example after a submit.

    import { Link, NavLink, useNavigate } from 'react-router'; export function Nav() {  return (    <nav>      <Link to="/">Home</Link>…
  4. 04

    How do nested routes and layouts work in React Router, and what does <Outlet> do?

    Child routes are nested inside a parent route, and the parent renders <Outlet /> where the matched child should appear.

    const router = createBrowserRouter([  {    path: '/projects/:projectId',    Component: ProjectLayout,    children: [      { index: true, Component: Overview }, // /projects/7…
  5. 05

    How do you define and read dynamic URL params in React Router?

    Mark a segment with a colon in the path (users/:userId) and read it with useParams() in the component or from params in the loader.

    import { useParams } from 'react-router'; // route: { path: 'users/:userId', Component: UserPage }export function UserPage() {  const { userId } = useParams<{ userId: string }>();  const id = Number(userId);…
  6. 06

    What is the difference between browser, hash and memory routers, and why does a refreshed deep link return 404 on static hosting?

    createBrowserRouter uses real paths through the History API, createHashRouter keeps the route after # so the server never sees it, and createMemoryRouter keeps history in memory for tests and embedded widgets.

  7. 07

    How do you read and update search params with useSearchParams?

    useSearchParams() returns a URLSearchParams for the current query string and a setter that navigates to a new query.

    import { useSearchParams } from 'react-router'; export function ProductSearch() {  const [params, setParams] = useSearchParams();  const q = params.get('q') ?? '';  const page = Math.max(1, Number(params.get('page')) || 1);…
  8. 08

    Find the bug: choosing a sort order wipes the search query and the page number from the URL.

    function SortSelect() {  const [params, setParams] = useSearchParams();  return (    <select      value={params.get('sort') ?? 'new'}      onChange={(e) => setParams({ sort: e.target.value })}    >      <option value="new">Newest</option>      <option value="price">Price</option>    </select>  );}

    setParams({ sort }) replaces the entire query string with that object, so q and page are dropped.

  9. 09

    What are loaders in React Router, and how are they better than fetching data in useEffect?

    A loader is a function on a route that the router calls with params and the request before rendering that route; the component reads the result with useLoaderData().

    import { createBrowserRouter, useLoaderData, type LoaderFunctionArgs } from 'react-router'; async function userLoader({ params, request }: LoaderFunctionArgs) {  const res = await fetch(`/api/users/${params.userId}`, { signal: request.signal });  if (!res.ok) throw new Response('Not found', { status: 404 });  return (await res.json()) as User;…
  10. 10

    What are actions and <Form> in React Router, and what happens after a submission?

    An action is a route function that handles a non-GET submission: <Form method="post"> serializes the fields, the router calls the route's action with the request, and when it finishes, all loaders on the page revalidate, so the UI shows fresh data without manual refetching.

    import { Form, redirect, useActionData, useNavigation, type ActionFunctionArgs } from 'react-router'; export async function action({ request }: ActionFunctionArgs) {  const form = await request.formData();  const title = String(form.get('title') ?? '').trim();  if (!title) return { error: 'Title is required' };…
  11. 11

    Find the bug: going from /users/1/edit to /users/2/edit updates the URL, but the form still shows user 1's name and email.

    // route: { path: 'users/:userId/edit', loader: userLoader, Component: EditUser }export function EditUser() {  const user = useLoaderData<typeof userLoader>();  const [name, setName] = useState(user.name);  const [email, setEmail] = useState(user.email);   return (    <form>      <input value={name} onChange={(e) => setName(e.target.value)} />      <input value={email} onChange={(e) => setEmail(e.target.value)} />    </form>  );}

    Both URLs match the same route, so React Router keeps EditUser mounted and only hands it new loader data.

  12. 12

    How do you implement protected routes in React Router?

    Wrap private routes in a layout route that checks the session and either renders <Outlet /> or redirects to the login page, remembering where the user was going.

    import { createBrowserRouter, redirect, type LoaderFunctionArgs } from 'react-router'; async function requireUser({ request }: LoaderFunctionArgs) {  const user = await getSession();  if (!user) {    const next = new URL(request.url).pathname;…
  13. 13

    Find the bug: the account page flashes on screen for a moment before a signed-out user is redirected to login.

    export function RequireAuth() {  const { user } = useSession();  const navigate = useNavigate();   useEffect(() => {    if (!user) navigate('/login');  }, [user, navigate]);   return <Outlet />;}

    The guard always renders <Outlet /> and redirects only in an effect, which runs after the private page has been committed and painted.

  14. 14

    How do you warn the user before leaving a page with unsaved changes in React Router?

    Use two mechanisms for two kinds of exit: useBlocker stops navigations inside the app (links, navigate, Back and Forward) and lets you show your own dialog; a beforeunload listener covers reloading, closing the tab and typing another address, where the browser shows its own prompt.

    import { useEffect, useState } from 'react';import { useBlocker } from 'react-router'; export function useUnsavedChangesGuard(isDirty: boolean) {  // in-app navigation: links, navigate(), Back and Forward  const blocker = useBlocker(…
  15. 15

    How do you code-split by route in React Router?

    Load each route's code only when it is visited: in data mode use the route's lazy property, which imports the component, loader and action on navigation; in declarative mode wrap React.lazy components in <Suspense>.

    import { createBrowserRouter } from 'react-router'; export const router = createBrowserRouter([  { path: '/', Component: Home }, // stays in the main bundle  {    path: 'reports/:id',…
  16. 16

    How do you handle errors and 404 pages in React Router?

    Give routes an ErrorBoundary (or errorElement): anything thrown in that route's loader, action or render shows it in place of the route, inside the parent layout.

    import { data, isRouteErrorResponse, useRouteError, type LoaderFunctionArgs } from 'react-router'; export async function loader({ params }: LoaderFunctionArgs) {  const post = await getPost(params.slug!);  if (!post) throw data('Post not found', { status: 404 });  return post;…
  17. 17

    How do you show pending UI during navigation, and how do you keep a slow loader from blocking the page?

    useNavigation() exposes the global state (idle, loading, submitting) for a progress bar.

    import { Suspense } from 'react';import { Await, useLoaderData, useNavigation, type LoaderFunctionArgs } from 'react-router'; export async function loader({ params }: LoaderFunctionArgs) {  const comments = getComments(params.id!); // not awaited: streamed later  const post = await getPost(params.id!); // awaited: the navigation waits for it…
  18. 18

    Why does a new page sometimes open scrolled halfway down after a client-side navigation, and how does <ScrollRestoration> fix it?

    With client-side routing the document never changes, so the window keeps the previous page's scroll position, and on Back the browser restores the old offset before the data has rendered.

    import { Link, Outlet, ScrollRestoration } from 'react-router'; export function Root() {  return (    <>      <Header />…
  19. 19

    What changed in React Router v7 compared with v6, and how do you migrate?

    v7 is a non-breaking upgrade for apps that enabled all v6 future flags: the packages merge into react-router, Remix becomes its framework mode, and loaders return plain objects instead of json().

  20. 20

    What happens to in-flight loaders when the user navigates again, and how does React Router decide what to revalidate?

    React Router keeps only the latest navigation: a new click aborts the pending one's loaders through request.signal, so stale responses never reach the UI.

    import type { LoaderFunctionArgs, ShouldRevalidateFunctionArgs } from 'react-router'; export async function loader({ params, request }: LoaderFunctionArgs) {  // aborted automatically if the user navigates away before it finishes  const res = await fetch(`/api/projects/${params.id}/sidebar`, { signal: request.signal });  if (!res.ok) throw new Response('Failed to load', { status: res.status });…
  21. 21

    What does React Router framework mode add, and when would you choose it over data mode or Next.js?

    Framework mode is React Router plus the former Remix: a Vite plugin with routes.ts, server loader and action, SSR, prerendering or SPA output, automatic code splitting and generated route types.

    // app/routes/product.tsximport type { Route } from './+types/product'; export async function loader({ params }: Route.LoaderArgs) {  // runs only on the server: the database client never ships to the browser  const product = await db.product.findUnique({ where: { id: params.id } });…