import { Link } from "@cloudflare/kumo";

export function LinkBasicDemo() {
  return (
    <div className="flex flex-wrap items-center gap-x-6 gap-y-4 text-base">
      <Link href="#">Underlined inline link</Link>
      <Link
        href="https://cloudflare.com"
        target="_blank"
        rel="noopener noreferrer"
      >
        External link <Link.ExternalIcon />
      </Link>
    </div>
  );
}

Installation

Barrel

import { Link } from "@cloudflare/kumo";

Granular

import { Link } from "@cloudflare/kumo/components/link";

Usage

Link renders an underlined anchor that inherits its color from the surrounding text. There is a single visual style — Kumo intentionally does not ship a distinct “brand blue” link color.

import { Link } from "@cloudflare/kumo";

export default function Example() {
  return (
    <p>
      Read our <Link href="/docs">documentation</Link> for more details.
    </p>
  );
}

Use the Link.ExternalIcon subcomponent to indicate links that open in a new tab.

import { Link } from "@cloudflare/kumo";

export default function Example() {
  return (
    <Link
      href="https://cloudflare.com"
      target="_blank"
      rel="noopener noreferrer"
    >
      Visit Cloudflare <Link.ExternalIcon />
    </Link>
  );
}

Framework Integration (LinkProvider)

For app-wide router integration, configure a LinkProvider at your app root. Your wrapper component receives href and is responsible for bridging to your router’s API. This lets engineers use <Link href="..."> everywhere without thinking about routing internals.

import { forwardRef } from "react";
import { LinkProvider } from "@cloudflare/kumo";
import { Link as RouterLink } from "react-router-dom";

// Your app's wrapper maps href to the router's navigation prop
// and handles external URLs with a plain <a>
const AppLink = forwardRef(({ href, to, ...rest }, ref) => {
  const destination = href ?? to;
  const isExternal =
    destination?.startsWith("http") &&
    new URL(destination).origin !== window.location.origin;

  if (isExternal) {
    return <a ref={ref} href={destination} {...rest} />;
  }
  return <RouterLink ref={ref} to={destination} {...rest} />;
});

// Wrap your app once
export function App() {
  return (
    <LinkProvider component={AppLink}>
      {/* All <Link href="..."> calls go through AppLink */}
      <YourApp />
    </LinkProvider>
  );
}

Composition with render prop

For exceptional cases where you need direct control over the rendered element, use the render prop. This bypasses the LinkProvider entirely — all other props (href, target, className, etc.) are merged onto the provided element automatically.

import { Link } from "@cloudflare/kumo";
import { Link as RouterLink } from "react-router-dom";

export default function Example() {
  return (
    <>
      {/* Force a specific router link (bypasses LinkProvider) */}
      <Link render={<RouterLink to="/dashboard" />}>Dashboard</Link>

      {/* Force a plain anchor (bypasses LinkProvider) */}
      <Link render={<a />} href="https://example.com" target="_blank" rel="noopener noreferrer">
        External Site <Link.ExternalIcon />
      </Link>
    </>
  );
}

Examples

Inline in Paragraph

Links flow naturally within paragraph text with proper underline offset.

This is a paragraph with an inline link that flows naturally with the surrounding text. Links maintain proper underline offset for readability.

import { Link } from "@cloudflare/kumo";

export function LinkInParagraphDemo() {
  return (
    <p className="mx-auto max-w-md text-base leading-relaxed text-kumo-default">
      This is a paragraph with an <Link href="#">inline link</Link> that flows
      naturally with the surrounding text. Links maintain proper underline
      offset for readability.
    </p>
  );
}

Use Link.ExternalIcon to visually indicate links that navigate away from your site.

import { Link } from "@cloudflare/kumo";

export function LinkExternalDemo() {
  return (
    <Link
      href="https://cloudflare.com"
      target="_blank"
      rel="noopener noreferrer"
      className="text-base"
    >
      Visit Cloudflare <Link.ExternalIcon />
    </Link>
  );
}

Color Inheritance

Link inherits color from its parent, which makes it work naturally inside colored contexts such as alerts and error messages.

This error message contains a link that inherits the red color from its parent.

import { Link } from "@cloudflare/kumo";

export function LinkColorInheritanceDemo() {
  return (
    <p className="text-base text-kumo-danger">
      This error message contains a <Link href="#">link</Link> that inherits
      the red color from its parent.
    </p>
  );
}

Composition with render prop

The render prop lets you compose Link styling onto any element, enabling integration with framework routing components.

import { Link } from "@cloudflare/kumo";

export function LinkRenderDemo() {
  return (
    <div className="flex flex-col gap-x-6 gap-y-4 text-base md:flex-row">
      <Link render={<CustomRouterLink href="/dashboard" />}>
        Dashboard (via render)
      </Link>
      <Link
        render={
          <CustomRouterLink
            href="https://developers.cloudflare.com"
            target="_blank"
            rel="noopener noreferrer"
          />
        }
      >
        Cloudflare Docs <Link.ExternalIcon />
      </Link>
    </div>
  );
}

API Reference

Extends all native anchor element attributes.

PropTypeDefaultDescription
variant”default""default”

Visual style. Currently a single underlined variant that inherits color from the surrounding text.

renderReactElement-

Element to render with Link props merged onto it

hrefstring-

Link destination URL. Use this for all links — both internal and external. Configure a LinkProvider to bridge href to your router.

tostring-

Deprecated. Use href instead. This prop will be removed in a future major version.

classNamestring-Additional CSS classes
childrenReactNode-Link content

Link.ExternalIcon

SVG icon component to indicate external links. Accepts all SVG element attributes.

<Link href="https://example.com" target="_blank" rel="noopener noreferrer">
  External Site <Link.ExternalIcon />
</Link>

Design Guidelines

Visual Style

  • Links are underlined and inherit their color from the surrounding text — there is no separate “brand blue” link color

  • Inside colored contexts (alerts, errors, callouts), the link automatically picks up the parent color

  • Always use Link.ExternalIcon for links that open in new tabs
  • Set target="_blank" and rel="noopener noreferrer" for security

  • The icon provides a visual cue that users will leave the current site

Framework Integration

  • Configure a LinkProvider at your app root to integrate with your client-side router

  • Your wrapper receives href and maps it to your router’s navigation prop (e.g. React Router’s to)

  • The wrapper should handle external URLs by rendering a plain &lt;a&gt; instead of routing them

  • Use the render prop as an escape hatch for exceptional cases that need direct control over the rendered element

  • The to prop is deprecated — use href for all link destinations

Accessibility

  • Links are keyboard focusable by default
  • The external icon has aria-hidden="true" - add descriptive text for screen readers

  • Ensure sufficient color contrast with the surrounding text
  • Use descriptive link text (avoid “click here”)