<!-- Generated by scripts/gen-docs.mts from the JSDoc in registry/. Do not edit; run `pnpm run docs`. -->

# Link

A text link that navigates through your router. Internal `href`s render through `OuroProvider`'s `linkComponent`, the same path `Button` with `href` takes, and `external` opens a new tab safely and says so.

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

## Example

```tsx
<Link href="/recipes">Browse recipes</Link>
<Link href="https://ouro.bot" external>Ouro</Link>
<Link href="/mcp" reloadDocument>MCP server metadata</Link>
<Text>Read the <Link href="/terms">terms</Link> first.</Text>
<Text size="sm"><Link href="/forgot" inline={false}>Forgot password?</Link></Text>
```

## Usage

Link is for text links only: it sets the text color and the underline, so wrapping a card, a list row or a tab in it restyles their text. Inside running text, put the Link in a `Text`.

On a touch screen a link that stands alone is a box at least 44px tall and wide, with its text centred, so it is easy to tap and never overlaps a neighbour. A link in running text (a `<p>`, a `<label>`, a `Text`, a field's description or error) stays as tall as its line; `inline` says the same anywhere else. With a mouse every link is as tall as its text.

Keyboard focus draws an outline around a link that stands alone.

## Use instead when

- Use `Button` with `href` instead when the link is the main action and should look like a button.
- Use `Card` with `href` instead when a whole card is the link, `Item` with `href` for a whole list row, and `Table` with `rowHref` (or `TableRow` with `href`) for a table row: each makes the surface one link with one tab stop, through the same router link, and keeps its own look.
- Use `Navbar` or `Sidebar` instead for the links that move between an app's pages, which mark the current one: `Tabs` and `SegmentedControl` switch a view in place and take no `href`.
- In running text it thickens the underline in the ring's colour instead, since an outline would cover the next character.

## Props

Type: `LinkProps`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` |  | The link text. Make it say where the link goes: "Browse recipes", not "click here". |
| `className` | `string` |  | Layout only: margins, display, grid placement (`ms-2`). Appearance classes warn in development; use `tone`. |
| `external` | `boolean` |  | Opens the link in a new tab: sets `target="_blank"`, adds `rel="noopener noreferrer"` (keeping your own `rel` tokens) and an icon, and tells screen readers "opens in a new tab". |
| `href` (required) | `string` |  | Where the link goes. Internal paths render through `OuroProvider`'s `linkComponent` (your router's link), so navigation stays client-side; `http(s)://`, `mailto:` and other absolute URLs render as a plain `<a>`. Router links that take `to` take `href` here: the adapter maps it. |
| `inline` | `boolean` |  | Whether the link sits in running text. On a touch screen a link that stands alone ("Forgot password?", "Back to recipes", a link in a list item or a table cell) is a box at least 44px tall and wide with its text centred, which takes its room in the layout; a link in running text stays as tall as its line. Unset, a link is in running text inside a `<p>`, a `<label>`, a `<dt>` or `<dd>`, a `Text`, and Ouro's own text (a field's description and error, a Form's message, an Alert's text, an Item's description). Pass `inline` for a sentence in another element, and `inline={false}` for a link that is alone in its `Text`. |
| `ref` | `Ref<HTMLAnchorElement>` |  | Reaches the rendered element. |
| `reloadDocument` | `boolean` |  | Navigates with a full page load: renders a plain `<a>` for an internal path too, skipping `OuroProvider`'s `linkComponent`, so your router never handles it. Use it for a path your router has no page for: a resource route (`/mcp`, `/feed.xml`), a file download, a sign-in that starts on the server (`/auth/github`). A link with `download` does the same. |
| `tone` | `"primary" \| "secondary" \| "destructive" \| "success" \| "warning"` | `"primary"` | Text color from the theme, named after its token. `secondary` is the page's foreground, for links in navigation and lists; the underline still marks it as a link. `primary` reads `--primary`, not a theme's `--ouro-action`: a link is text in a sentence and the action colour is a fill. For a link that looks like the page's main action, use `<Button href>`. |

## Native attributes

Also accepts the attributes in React's `AnchorHTMLAttributes` and `HTMLAttributes` (`id`, `aria-*`, event handlers), passed to the rendered element, except those named on this page.

## Don't use

These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.

- `color`: Not a prop. Ouro UI uses `tone`: `<Link tone="destructive">`.
- `isExternal`: Not a prop. Ouro UI uses `external`: `<Link href="https://…" external>`.
- `to`: Not a prop. Ouro UI uses `href`, for router links too: `<Link href="/recipes">`.

## `className` is for layout only

Pass margins, width and grid placement (`mt-4`, `w-full`, `col-span-2`). Your classes always win over Ouro UI's: Link merges `className` last with `cn` (tailwind-merge), so a class that conflicts with its own replaces it.[^layout-classes]

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (`hover:`, `sm:`) and any `!` are removed, it starts with `bg-`, `text-`, `border-`, `rounded-`, `shadow-`, `font-`, `ring-`, `from-`, `via-`, `to-`, `fill-` or `stroke-`, or is `border`, `rounded`, `shadow` or `ring`, or has a `dark:` variant. Text wrapping, overflow and table layout classes (`text-wrap`, `text-ellipsis`, `border-collapse`) are layout and do not warn.

Alignment classes (`text-center`) warn too: set alignment on a parent element.

[^layout-classes]: test:registry/ouro/components/ouro/link/link.test.tsx::Link > lets app layout classes win verify:testExists(registry/ouro/components/ouro/link/link.test.tsx::Link > lets app layout classes win)
