Link
An inline link that is underlined as well as blue, says where it goes and breaks long URLs.
Orders over Q500 ship free. See the shipping rates for everything else.
Read the ssntl/ui documentation (opens in a new tab) before you install.
Tracking: https://ssntl.app/orders/2026-10-28/GT-48210/tracking?ref=confirmation-email
Installation
$ pnpm dlx shadcn@latest add @ssntl/linkUsage
import { Link } from "@/components/ui/link"
<p>See the <Link href="/shipping">shipping rates</Link> for everything else.</p>
<Link href="https://ui.ssntl.app/docs" external>ssntl/ui documentation</Link>
<Link asChild><NextLink href="/pricing">Pricing</NextLink></Link>- Obvious: blue, medium weight and underlined, so a link never relies on color alone.
- Descriptive: the text names the destination (“shipping rates”), never “click here” or “read more”.
- Long URLs break where they need to instead of pushing the layout wider.
- external opens a new tab, adds the arrow, rel="noopener noreferrer" and “(opens in a new tab)” for screen readers.
Code
import * as React from "react"
import { ArrowUpRight } from "lucide-react"
import { Slot } from "radix-ui"
import { cn } from "@/lib/utils"
// Essential Design links: blue-600 (blue-300 in dark), medium weight and
// always underlined, so a link never depends on color alone. The underline
// starts soft and fills in on hover. Long URLs break instead of overflowing.
function Link({
asChild = false,
external = false,
newTabLabel = "(opens in a new tab)",
className,
children,
...props
}: React.ComponentProps<"a"> & {
/** Render the child element (a framework Link, for example) with these styles. */
asChild?: boolean
/** Opens in a new tab: adds the arrow, rel and a label for screen readers. */
external?: boolean
/** Read after the text of an external link. */
newTabLabel?: string
}) {
const Comp = asChild ? Slot.Root : "a"
return (
<Comp
data-slot="link"
{...(external && { target: "_blank", rel: "noopener noreferrer" })}
className={cn(
"rounded-xs font-medium wrap-break-word text-blue-600 underline decoration-blue-600/40 underline-offset-4 dark:text-blue-300 dark:decoration-blue-300/40",
"transition-[text-decoration-color] duration-(--duration-fast) ease-out hover:decoration-current",
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
className
)}
{...props}
>
<Slot.Slottable>{children}</Slot.Slottable>
{external && (
<>
<ArrowUpRight
aria-hidden
strokeWidth={1.6}
className="ms-0.5 inline size-4 align-text-bottom"
/>
<span className="sr-only"> {newTabLabel}</span>
</>
)}
</Comp>
)
}
export { Link }