---
layout: ~/layouts/MdxDocLayout.astro
title: "Link"
description: "A styled anchor component for inline text links with multiple variants and composition support."
sourceFile: "components/link"
---

import ComponentExample from "~/components/docs/ComponentExample.astro";
import ComponentSection from "~/components/docs/ComponentSection.astro";
import {
  LinkBasicDemo,
  LinkInParagraphDemo,
  LinkExternalDemo,
  LinkCurrentVariantDemo,
  LinkRenderDemo,
} from "~/components/demos/LinkDemo";

{/* Hero Demo */}

<ComponentSection>
  <ComponentExample demo="LinkBasicDemo">
    <LinkBasicDemo client:visible />
  </ComponentExample>
</ComponentSection>

{/* Installation */}

<ComponentSection>

## Installation

### Barrel

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

### Granular

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

</ComponentSection>

{/* Usage */}

<ComponentSection>

## Usage

### Basic Link

  <p>
    The default Link component renders an underlined anchor with primary color styling.
  </p>

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

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

### External Links

<p>
  Use the `Link.ExternalIcon` subcomponent to indicate links that open in a new
  tab.
</p>

```tsx
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)

<p>
  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.
</p>

```tsx
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

<p>
  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.
</p>

```tsx
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" />} variant="inline">
        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>
    </>
  );
}
```

</ComponentSection>

{/* Examples */}

<ComponentSection>

## Examples

### Inline in Paragraph

<p>Links flow naturally within paragraph text with proper underline offset.</p>
<ComponentExample demo="LinkInParagraphDemo">
  <LinkInParagraphDemo client:visible />
</ComponentExample>

### External Link with Icon

<p>
  Use `Link.ExternalIcon` to visually indicate links that navigate away from
  your site.
</p>
<ComponentExample demo="LinkExternalDemo">
  <LinkExternalDemo client:visible />
</ComponentExample>

### Current Variant (Color Inheritance)

<p>
  The `current` variant inherits color from its parent, useful for links within
  colored contexts like alerts.
</p>
<ComponentExample demo="LinkCurrentVariantDemo">
  <LinkCurrentVariantDemo client:visible />
</ComponentExample>

### Composition with render prop

<p>
  The `render` prop lets you compose Link styling onto any element, enabling
  integration with framework routing components.
</p>
<ComponentExample demo="LinkRenderDemo">
  <LinkRenderDemo client:visible />
</ComponentExample>

</ComponentSection>

{/* API Reference */}

<ComponentSection>

## API Reference

### Link Props

<p>Extends all native anchor element attributes.</p>
<div class="overflow-x-auto">
  <table class="w-full text-sm">
    <thead>
      <tr class="border-b border-kumo-hairline">
        <th class="px-4 py-3 text-left font-semibold">Prop</th>
        <th class="px-4 py-3 text-left font-semibold">Type</th>
        <th class="px-4 py-3 text-left font-semibold">Default</th>
        <th class="px-4 py-3 text-left font-semibold">Description</th>
      </tr>
    </thead>
    <tbody>
      <tr class="border-b border-kumo-hairline">
        <td class="px-4 py-3 font-mono text-xs">variant</td>
        <td class="px-4 py-3 font-mono text-xs">
          "inline" | "current" | "plain"
        </td>
        <td class="px-4 py-3 font-mono text-xs">"inline"</td>
        <td class="px-4 py-3 text-xs">Visual style variant</td>
      </tr>
      <tr class="border-b border-kumo-hairline">
        <td class="px-4 py-3 font-mono text-xs">render</td>
        <td class="px-4 py-3 font-mono text-xs">ReactElement</td>
        <td class="px-4 py-3 font-mono text-xs">-</td>
        <td class="px-4 py-3 text-xs">
          Element to render with Link props merged onto it
        </td>
      </tr>
      <tr class="border-b border-kumo-hairline">
        <td class="px-4 py-3 font-mono text-xs">href</td>
        <td class="px-4 py-3 font-mono text-xs">string</td>
        <td class="px-4 py-3 font-mono text-xs">-</td>
        <td class="px-4 py-3 text-xs">
          Link destination URL. Use this for all links — both internal and
          external. Configure a `LinkProvider` to bridge `href` to your
          router.
        </td>
      </tr>
      <tr class="border-b border-kumo-hairline">
        <td class="px-4 py-3 font-mono text-xs">to</td>
        <td class="px-4 py-3 font-mono text-xs">string</td>
        <td class="px-4 py-3 font-mono text-xs">-</td>
        <td class="px-4 py-3 text-xs">
          <strong>Deprecated.</strong> Use `href` instead. This prop will
          be removed in a future major version.
        </td>
      </tr>
      <tr class="border-b border-kumo-hairline">
        <td class="px-4 py-3 font-mono text-xs">className</td>
        <td class="px-4 py-3 font-mono text-xs">string</td>
        <td class="px-4 py-3 font-mono text-xs">-</td>
        <td class="px-4 py-3 text-xs">Additional CSS classes</td>
      </tr>
      <tr class="border-b border-kumo-hairline">
        <td class="px-4 py-3 font-mono text-xs">children</td>
        <td class="px-4 py-3 font-mono text-xs">ReactNode</td>
        <td class="px-4 py-3 font-mono text-xs">-</td>
        <td class="px-4 py-3 text-xs">Link content</td>
      </tr>
    </tbody>
  </table>
</div>

### Variants

<div class="overflow-x-auto">
  <table class="w-full text-sm">
    <thead>
      <tr class="border-b border-kumo-hairline">
        <th class="px-4 py-3 text-left font-semibold">Variant</th>
        <th class="px-4 py-3 text-left font-semibold">Description</th>
        <th class="px-4 py-3 text-left font-semibold">Use Case</th>
      </tr>
    </thead>
    <tbody>
      <tr class="border-b border-kumo-hairline">
        <td class="px-4 py-3 font-mono text-xs">inline</td>
        <td class="px-4 py-3 text-xs">Primary color with underline</td>
        <td class="px-4 py-3 text-xs">Default for inline text links</td>
      </tr>
      <tr class="border-b border-kumo-hairline">
        <td class="px-4 py-3 font-mono text-xs">current</td>
        <td class="px-4 py-3 text-xs">
          Inherits parent text color with underline
        </td>
        <td class="px-4 py-3 text-xs">
          Links within colored contexts (alerts, errors)
        </td>
      </tr>
      <tr class="border-b border-kumo-hairline">
        <td class="px-4 py-3 font-mono text-xs">plain</td>
        <td class="px-4 py-3 text-xs">Primary color without underline</td>
        <td class="px-4 py-3 text-xs">Navigation links, menus, footers</td>
      </tr>
    </tbody>
  </table>
</div>

### Link.ExternalIcon

<p>
  SVG icon component to indicate external links. Accepts all SVG element
  attributes.
</p>

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

</ComponentSection>

{/* Design Guidelines */}

<ComponentSection>

## Design Guidelines

  <div class="space-y-4 text-sm">
    <div>

      ### When to Use Each Variant

      <ul class="ml-4 list-disc space-y-1">
        <li>
          <strong>inline</strong>: Default choice for links within body text
        </li>
        <li>
          <strong>current</strong>: Links inside alerts, banners, or other
          colored containers
        </li>
        <li>
          <strong>plain</strong>: Navigation menus, footers, or where underlines
          are distracting
        </li>
      </ul>
    </div>
    <div>

      ### External Link Indicators

      <ul class="ml-4 list-disc space-y-1">
        <li>Always use `Link.ExternalIcon` for links that open in new tabs</li>
        <li>
          Set `target="_blank"` and `rel="noopener noreferrer"` for security
        </li>
        <li>
          The icon provides a visual cue that users will leave the current site
        </li>
      </ul>
    </div>
    <div>

      ### Framework Integration

      <ul class="ml-4 list-disc space-y-1">
        <li>
          Configure a `LinkProvider` at your app root to integrate with your
          client-side router
        </li>
        <li>
          Your wrapper receives `href` and maps it to your router's
          navigation prop (e.g. React Router's `to`)
        </li>
        <li>
          The wrapper should handle external URLs by rendering a plain
          `&lt;a&gt;` instead of routing them
        </li>
        <li>
          Use the `render` prop as an escape hatch for exceptional cases
          that need direct control over the rendered element
        </li>
        <li>
          The `to` prop is deprecated — use `href` for all link destinations
        </li>
      </ul>
    </div>
    <div>

      ### Accessibility

      <ul class="ml-4 list-disc space-y-1">
        <li>Links are keyboard focusable by default</li>
        <li>
          The external icon has `aria-hidden="true"` - add descriptive text for
          screen readers
        </li>
        <li>Ensure sufficient color contrast for all variants</li>
        <li>Use descriptive link text (avoid "click here")</li>
      </ul>
    </div>
  </div>
</ComponentSection>
