Skip to main content
BM

Link

Navigation to another page or site, optionally styled as a button.

View source on GitHub

Every color and size on this site comes from a design token.

Source code on GitHub, opens in new tab

LinkExample.tsx
import { Link } from "@/components/Link";

export function LinkExample() {
  return (
    <>
      <p>
        Every color and size on this site comes from a{" "}
        <Link href="/design-system/tokens">design token</Link>.
      </p>
      <p>
        <Link href="https://github.com/BeMorr16/Santoryu" newTab>
          Source code on GitHub
        </Link>
      </p>
    </>
  );
}

Examples

Inline, in running text

Read about the design tokens, or email hello@example.com.

New tab

Visited (opt-in)

Design System and Design tokens

Visited links turn violet.

Current page

This page

Announced as: “This page, link, current page”

This page, unmarked

Announced as: “This page, unmarked, link”

Underline on hover

Customized with a component token

CSS
.quietLinks {
  --component-link-color: var(--color-text-default);
  --component-link-color-hover: var(--color-link-hover);
}

Props

Any native prop not listed here goes to the root element unchanged.

Link props
PropTypeDefaultDescription
hrefstring—Required. A /path routes client-side; anything else is a plain link.
newTabbooleanfalseOpens in a new tab, with an icon and “, opens in new tab” in its name.
relstring—Merged with noopener noreferrer when newTab is set, e.g. rel="me".
appearance"inline" | "button""inline"inline is an underlined text link; button looks like a Button.
showVisitedbooleanfalseInline only. Colors the link violet once visited.
currentMatch"exact" | "section""exact"Inline only. Routed inline links set aria-current themselves: "page" on an exact match, and with "section", "true" on pages below it. Your own aria-current overrides it. The hash is ignored, so use #id hrefs for in-page links.
underline"always" | "hover" | "none""always"Where the underline shows. Inline links only.
variant"primary" | "secondary" | "ghost""secondary"With appearance="button" only.
size"sm" | "md""md"With appearance="button" only.
iconStartIconName—With appearance="button" only.
iconEndIconName—With appearance="button" only.
nameTooltipbooleantrueWith appearance="button" only. An icon-only link shows its name (without the new-tab text) as a tooltip; false turns it off. None for a link named by aria-labelledby, whose name is already on screen.
prefetch"intent" | "render" | "none" | "viewport"—React Router's option, for /path links only (ignored otherwise).
replaceboolean—React Router's option, for /path links only (ignored otherwise).
stateany—React Router's option, for /path links only (ignored otherwise).
preventScrollResetboolean—React Router's option, for /path links only (ignored otherwise).
viewTransitionboolean—React Router's option, for /path links only (ignored otherwise).
reloadDocumentboolean—React Router's option, for /path links only (ignored otherwise).
discover"render" | "none"—React Router's option, for /path links only (ignored otherwise).

Component tokens

Link component tokens
TokenCopyDescriptionAliasesLightDarkPreview

Accessibility

  • Always an <a> with an href; there is no disabled link.
  • Always named: inline links need text, and a button-styled link with no text needs an icon plus aria-label or aria-labelledby (a type error otherwise). Types can't see empty children such as {undefined} or a lone decorative icon, so those still need care.
  • Routed inline links mark the current page with aria-current="page", and with currentMatch="section", pages below it with "true". Pass aria-current to override, including false. Button-styled links are only marked when you pass it.
  • The current page is shown by weight as well as color.
  • Inline links are underlined, so they're told apart from text by more than color. Hover thickens the underline.
  • underline="hover" drops the underline at rest. Use it only where the position marks the links (a nav, a site name), since color alone can't (WCAG 1.4.1). A current link keeps its underline.
  • underline="none" never underlines, even when current or hovered. Use it only where the position marks the links and something besides color marks the current one, such as the site bar's brackets (WCAG 1.4.1). A current link is still bold.
  • The visited color is opt-in and color-only, so nothing depends on it.
  • New-tab links end their name with “, opens in new tab”, also when named by aria-label or aria-labelledby, and the icon stays on the line of the last word.
  • The focus ring uses component.link.focus-ring-color.
Link keyboard
KeyAction
EnterFollows the link.