Link
The text link, in both house treatments. default and muted are the standalone UI link for chrome, warming to the brand accent on hover; prose is the hyperlink embedded in running copy, underlined at rest.
Installation#
Usage#
import { Link } from "@/components/ui/link"
export function Example() {
return <Link href="/pricing">See pricing</Link>
}Variants#
default rests at text-foreground (full-strength, reads as a real label); muted starts quiet for a value in a directory or a footer link. Both lift to the brand accent on hover. prose is the third treatment, for a link inside a sentence: it sets no rest color, so it inherits the copy around it, and only its underline marks it until hover turns both text and underline the accent.
In prose#
A hyperlink inside running copy is a different animal from a link in UI chrome, so prose is a treatment rather than a color: it inherits the paragraph's color, wears a hairline decoration-border underline at rest, and turns both the text and the underline the --link token on hover. It stays display: inline so it breaks across lines mid-sentence instead of wrapping as an unbreakable block, drops the trailing-icon reveal the standalone variants carry (a glyph in prose is content, not a hover affordance), and hovers on the faster 160ms beat, because a word-sized target in a paragraph is swept over constantly and a slow fade out leaves a trail of words cooling back down.
import NextLink from "next/link"
import { Link } from "@/components/ui/link"
export function Example() {
return (
<p className="text-muted-foreground">
Components are owned source in your repo. Start with the{" "}
{/* asChild keeps an internal prose link on client-side routing. */}
<Link asChild variant="prose">
<NextLink href="/docs/installation">installation guide</NextLink>
</Link>
.
</p>
)
}Underline#
The color shift carries the affordance in context, so the link is not underlined by default. Pass underline for a prose-like link that must read as underlined at rest. It applies to default and muted only: prose is underlined by definition, and pinning a thickness on it (the prop sets decoration-1) would stop the underline tracking the font metrics, so combining the two is a type error.
With an icon#
Drop a trailing glyph (a directional arrow for an outbound or routing link) beside the label. It reads as a hover affordance, so it stays hidden at rest and slides and fades in when the link is hovered or keyboard-focused, warming to brand with the text. The motion is a plain CSS transition (interruptible), and it collapses to an instant reveal under prefers-reduced-motion.
As child#
Use asChild to render the link styles onto another element, e.g. Next.js's Link, via Radix Slot, so you keep client-side routing.
import NextLink from "next/link"
import { Link } from "@/components/ui/link"
export function Example() {
return (
<Link asChild>
<NextLink href="/pricing">See pricing</NextLink>
</Link>
)
}