Skip to content

Action

Link

Beta

A text link, available on web (React) and native (React Native). Use inline (default) for a link that flows inside a paragraph of body copy, or standalone for a link on its own line. Navigates via href or performs an action via onClick/onPress — never both.

Modes

Inline (default)

Flows inside a paragraph. Underlined, inherits the surrounding text's weight and size.

Read our privacy policy for details.

<Text variant="body-md">
Read our <Link href="/privacy">privacy policy</Link> for details.
</Text>

External inline

Adds target="_blank" rel="noopener noreferrer" on web, auto-renders a trailing icon (web only — see Accessibility), and appends "(opens in a new tab)" to the accessible name on both platforms.

See the help centre.

<Text variant="body-md">
See the <Link href="https://checkatrade.com/help" external>help centre</Link>.
</Text>

Standalone

Weight 500, md size, underlined at rest (no underline on hover, underlined again on focus). Its own element, on its own line.

<Link inline={false} onClick={() => {}}>Show all trades</Link>

Standalone external

external auto-renders a fixed trailing icon — mutually exclusive with a manual trailingIcon.

<Link inline={false} href="https://checkatrade.com/help" external>Help centre</Link>

Subtle

Fixed navy colour, no colour change on hover/focus, no distinct visited colour. No underline at rest — gains one on hover/focus instead. Valid on inline or standalone.

<Link inline={false} onClick={() => {}} subtle>Show all trades</Link>

Inverse

Inside an inverse <Surface> this happens automatically — Surface is the source of truth. Pass inverse explicitly only for a dark or branded background that isn’t a Surface.

Inverse on a dark Surface

The rendered preview passes inverse explicitly as a docs-site-only workaround — this ComponentPreview's client:load island doesn't carry Surface's tone context to nested framework components, so Link's auto-detection can't run here. The documented API (below) needs no explicit inverse prop; Storybook and real consumer apps pick it up from Surface automatically.

<Surface tone="inverse" className="rounded-lg p-6">
<Link href="/help">Login help</Link>
</Surface>

When to use

Use Link for a text link — either inline inside a sentence of body copy, or as a standalone navigation/action link on its own line. Provide exactly one of href (navigation) or onClick/onPress (action); the underlying element and accessible role follow automatically.

Modes

  • Inline (default): underlined in every state, inherits the surrounding text’s weight and size. Use inside a Text paragraph.
  • Standalone (inline={false}): weight 500, md size, optional leadingIcon/trailingIcon. Underline toggles by state (underlined at rest, none on hover, underlined again on focus) rather than being constantly on/off. Use for a link that stands on its own — not embedded in a sentence.

Read our privacy policy for details.

Do

Use inline for a link embedded mid-sentence in body copy.

Read our privacy policy for details.

Don't

Don't use a standalone link mid-paragraph — it breaks the reading flow and doesn't inherit the surrounding text size.

When not to use

  • Button-styled actions: use Button for a primary/secondary call to action with a filled or outlined shell — Link is text-only.
  • Icon-only actions: use IconButton.

Props

Prop Type Default Description
children * string - Visible link text. String only — an inline link flows inside surrounding copy.
inline boolean true false renders a standalone link (weight 500, md size, underline toggles by state).
subtle boolean false Fixed navy colour, no colour change on hover/focus, no distinct visited colour. Standalone only (inline={false}) — a type error otherwise. Ignored when the link resolves to inverse.
href string - Navigation target. XOR with onClick/onPress. Web renders a real <a href>; native opens via Linking.openURL. Role "link".
onClick () => void - Web — action handler. XOR with href. Renders a link-styled <button>. Role "button".
onPress () => void - Native — action handler. XOR with href. Role "button".
external boolean false Web: adds target="_blank" rel="noopener noreferrer", auto-renders a trailing icon (both modes), appends "(opens in a new tab)" to the accessible name. Native: auto-renders a trailing icon on standalone only (no visual icon inline — see Accessibility), appends the same suffix to accessibilityLabel on both modes. Mutually exclusive with trailingIcon.
leadingIcon ComponentType<SVGProps<SVGSVGElement>> (web) / ComponentType<SvgProps> (native) - Standalone only (inline={false}). A glyph from @checkatrade/icons.
trailingIcon ComponentType<SVGProps<SVGSVGElement>> (web) / ComponentType<SvgProps> (native) - Standalone only (inline={false}); mutually exclusive with external. A glyph from @checkatrade/icons.
inverse boolean surrounding Surface tone Swaps to inverse token colours. Defaults to the surrounding Surface tone — pass explicitly to force it on a dark background that is not a Surface.
aria-label string children Web — accessible-name override.
accessibilityLabel string children Native — accessible-name override.
data-testid string - Web — passed to the root element for test targeting.
testID string - Native — passed to the root element for test targeting.

Import

Web

import { Link } from '@checkatrade/components-web';
import '@checkatrade/components-web/css';

Native

import { Link } from '@checkatrade/components-native';

Basic usage

<Text variant="body-md">
  Read our <Link href="/privacy">privacy policy</Link> for details.
</Text>

<Link inline={false} onClick={() => {}}>
  Show all trades
</Link>

Colour

Real link colour tokens landed in @checkatrade/tokens 0.14.0 — theme-aware (dark mode swaps the default/inverse ramps automatically, no manual workaround needed):

  • Default (not subtle, not inverse): rest #5263ff, hover/focus #3a3787, visited (web only) #681da8.
  • Inverse: rest #ffffff, hover/focus #f7f7f6, visited (web only) #fdf4ff.
  • Subtle: fixed #040154 always (the action-label-ghost ramp — this is what the whole component used everywhere before 0.14.0) — no colour change on hover/focus, no distinct visited colour. Standalone only (inline={false}) — a type error otherwise. Not available combined with inverse — silently ignored if the link resolves to inverse (Figma has no subtle+inverse variant).

href-based links use the real browser :visited pseudo-class automatically — no prop. Colour comes from the real visited tokens above. Skipped entirely when subtle (no distinct visited look). Native has no history API, so there’s no visited support there.

Browsers only allow a handful of colour properties (including color) to change under :visited, for privacy reasons — text-decoration-line isn’t one of them, so a visited standalone link’s underline can’t react to visited state; it keeps whatever the rest state’s underline already is.

Platform status

Platform / AreaStatus
Design (Figma) Beta
Web (React) Beta
Native (React Native) Beta
iOS (Swift) Planned
Android (Kotlin) Planned
Accessibility audit Planned

Accessibility

Web

  • href renders a real <a href> — announced as a link; supports cmd/right-click.
  • onClick renders a <button> — announced as a button.
  • Inline links are underlined in every state so they’re distinguishable by more than colour (WCAG). Standalone’s underline toggles by state instead (underlined at rest, none on hover, underlined again on focus — inverted when subtle).
  • Keyboard-only focus ring via a scoped [data-cat-link]:focus-visible rule.
  • external renders a visual icon on both inline and standalone (browsers support inline SVG inside <a>).

React Native

  • hrefaccessibilityRole="link", opens via Linking.openURL. onPressaccessibilityRole="button".
  • Inline renders as a nested Text node so it flows inside the surrounding paragraph.
  • Standalone renders as its own Pressable row with a keyboard-only focus ring on react-native-web.
  • external renders a visual icon on standalone only — RN’s text-flow engine only inline-nests Text/Image, not an SVG/View component, so an inline external link has no visual icon on native (an architectural RN limit, not a styling gap).

Both platforms

  • Tone is conveyed by an underline (inline, or standalone at rest/focus) in addition to colour.
  • external links get “(opens in a new tab)” appended to the accessible name on every platform and mode, even where the visual icon can’t render (native inline) — the accessibility warning is never platform-gated, only the icon is.
  • A visited standalone link’s underline can’t react to visited state — browsers only allow a handful of colour properties (including color) to change under :visited, never text-decoration-line. This is a browser privacy restriction, not a bug.

No releases yet.