skip to content

In React Router v7, what does <Link to="/settings"> do on a click that a plain <a href="/settings"> does not?

level: juniorimportance: must knowfreq 74%

answer

  1. still an anchor in the DOM
  2. the click handler, not the markup
  3. preventDefault, then a router navigation
  4. modifier keys and target left to the browser

basics

~20 s
<Link> renders a real <a> with a resolved href, but on a plain left-click it cancels the browser's page load and asks the router to navigate, so the page is not reloaded and the app's state and shared layouts survive.

solid answer

~40 s

`<Link>` renders an ordinary `<a>` whose `href` is the resolved target, so right-click, copy link and open-in-new-tab keep working. Its `onClick` is what differs: for an unmodified left-click on a link with no `target` (or `target="_self"`) it calls `event.preventDefault()` and runs a client-side navigation through the router instead of a document request. The JavaScript bundle and React state are kept, and any layout route that still matches keeps its component instance. Ctrl/Cmd/Shift/Alt clicks, middle clicks, `target="_blank"`, cross-origin absolute URLs and links with `reloadDocument` fall through to the browser. A plain `<a>` always triggers a full document load, which throws all of that away.

code

tsx · 20 lines
tsx
import { Link } from "react-router";

export function AccountMenu() {
  return (
    <nav>
      {/* client-side navigation, no page reload */}
      <Link to="/settings">Settings</Link>

      {/* server-handled URL: force a real document request */}
      <Link to="/logout" reloadDocument>
        Sign out
      </Link>

      {/* other origin: rendered as a normal external anchor */}
      <Link to="https://status.example.com" target="_blank" rel="noreferrer">
        Status page
      </Link>
    </nav>
  );
}

go deeper

for a junior

Recall that Link renders an anchor and navigates without a page reload, while a plain anchor reloads the whole app and loses its state.

for a middle

Explain the click handler: your onClick first, then the modifier-key and target checks, then preventDefault and a router navigation; mention reloadDocument and external URLs.

for a senior

Show judgment about when a real document load is correct, such as server-handled logout or a sibling app on the same origin, and why clickable divs hurt accessibility.

for a principal

Tie link discipline to the app's contract: consistent Link usage keeps navigation crawlable and accessible and lets the router own history, which matters when teams share one shell.

## What each element puts in the DOM In a React Router v7 app, both `<Link to="/settings">` and `<a href="/settings">` end up as an **anchor element** in the DOM. That is deliberate: an anchor is what browsers, screen readers, crawlers and users understand as a link. `<Link>` computes the `href` for you, resolving a relative `to` against the route it is rendered in and prefixing any `basename` the router was created with, so the markup is correct even when the app is mounted under a sub-path. The difference is not the markup, it is **who handles the click**. ## What happens on a click A plain `<a href>` hands the click to the browser, which performs a **document navigation**: it requests a new HTML page from the server, tears down the current page, re-downloads or re-evaluates the JavaScript and boots React again. Every piece of in-memory state is lost. `<Link>` attaches its own click handler. In order, it: 1. runs your own `onClick` prop first, if you passed one; if that handler calls `event.preventDefault()`, `<Link>` stops there and does nothing else; 2. checks whether this is a click the router should own: a primary-button click, with no Ctrl, Meta, Shift or Alt key held, on a link whose `target` is absent or `_self`; 3. if so, calls `event.preventDefault()` to cancel the document load and calls the router's `navigate` with the link's `to`, `replace`, `state`, `relative` and related options. The router then updates the history stack through the History API, matches the new location against the route tree and renders the new matches: routes that no longer match unmount, new ones mount, and a layout route that matches both the old and the new URL keeps its component instance, so a sidebar's open sections or a half-typed search box survive. ## Clicks `<Link>` deliberately leaves alone | Situation | Who handles it | |---|---| | Unmodified left-click, no `target` | the router (client-side navigation) | | Ctrl/Cmd/Shift/Alt click, middle click | the browser (new tab or window) | | `target="_blank"` or any non-`_self` target | the browser | | `to` is an absolute URL on another origin | the browser; the link is treated as external | | `reloadDocument` prop set | the browser performs a full document navigation | `reloadDocument` exists for the cases where you *want* a real page load, such as a logout URL handled by the server or a route served by a different application on the same origin. ## Two smaller behaviours worth knowing - **Same-URL clicks replace.** If the link points at the location already showing and you did not pass `replace`, `<Link>` replaces the current history entry instead of pushing a duplicate, mirroring what a browser does for an anchor to the current URL. Passing `replace={false}` forces a push. - **Same-origin absolute URLs are routed.** A `to` like `https://your-app.example/settings` whose origin matches the current page (and sits under the router's basename) is stripped to a path and handled client-side; only other origins are treated as external. ## Why not a `<div onClick>` with `useNavigate` Replacing links with clickable elements that call `navigate()` looks equivalent in a demo, but it loses everything the anchor provides: keyboard focus and Enter activation, the link role for assistive technology, the status-bar preview, the context menu, open-in-new-tab and crawlability. The React Router docs reserve `useNavigate` for navigations the user did not click, such as after a save completes. For anything the user clicks to go somewhere, render `<Link>` (or `<NavLink>` when it needs an active style). ## Mistakes interviewers listen for - **Mixing `<a href>` into internal navigation.** One stray anchor in a header silently reloads the app on every click; the symptom is lost form input and a flash of the loading screen. - **Wrapping `<Link>` in a button or a button in a `<Link>`.** Nested interactive elements are invalid HTML and confuse assistive technology; style the link instead. - **Calling `navigate()` from a link's `onClick` as well.** `<Link>` already navigates; a second call pushes a duplicate entry or races the first. - **Forgetting that `to` is relative.** `<Link to="settings">` rendered deep in the tree resolves against the route that renders it, which is often not the root; use a leading `/` when you mean an absolute path. - **Importing from the wrong package.** In v7 `Link` comes from `react-router`; `react-router-dom` only survives as a re-export for migration. ## Summary - Both render an `<a>`; `<Link>` resolves `to` into the `href`. - `<Link>` intercepts ordinary left-clicks and navigates inside the router with no document request. - It lets the browser handle modified clicks, other targets, external origins and `reloadDocument` links. - A plain `<a>` inside a React Router app reloads the whole application on every click.

  • Your onClick on a React Router <Link> calls event.preventDefault() for an unsaved-changes check. What happens to the navigation?
    `<Link>` runs your `onClick` first and only performs its own navigation when `event.defaultPrevented` is still false. So calling `preventDefault()` in your handler cancels both the browser's page load and the router's navigation. That makes it a simple way to veto one link, though a router-level blocker is the better tool for guarding every exit from a form.
  • Why does React Router still render an <a> element rather than a <button> for <Link>?
    Because navigation to another URL is link semantics. The anchor gives keyboard activation, the link role for screen readers, the URL preview, the context menu and open-in-new-tab, and it lets crawlers follow the URL. `<Link>` only swaps the default click behaviour; everything else about the anchor is kept intact.

A <Link> is like an office phone that looks and dials like any other phone: calls to an internal extension are routed inside the building's own exchange, while anything marked external goes out to the public network unchanged.

saying these in an interview costs you the question

  • Link renders a button or a div with a click handler, not an anchor
  • A plain <a href> behaves the same as Link inside a React Router app
  • Link reloads the page but serves it from a cache, so it is faster
  • Ctrl-clicking a Link navigates the current tab because the router owns all clicks
  • Clickable divs calling navigate() are an equivalent replacement for links