Link

Styled anchor with emphasis variants and an external-link affordance.

Report a bug

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Installation

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/link.json

Storybook

Explore all variants, controls, and accessibility checks in the interactive Storybook playground.

View in Storybook

4 stories available:

Code

import { Slot } from "@radix-ui/react-slot"; import { cva, type VariantProps } from "class-variance-authority"; import { ExternalLink } from "lucide-react"; import { cn } from "../../lib/utils"; const linkVariants = cva( "inline-flex items-center gap-1 rounded-sm underline-offset-4 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", { defaultVariants: { variant: "default", }, variants: { variant: { default: "font-medium text-primary hover:underline", muted: "text-muted-foreground hover:text-foreground hover:underline", underline: "font-medium text-primary underline hover:text-primary/80", }, }, }, ); /** Props for the {@link Link} component. */ export type LinkProps = { /** Render the styles onto the single child element instead of an `<a>`. */ asChild?: boolean; /** * Treat the link as external: opens in a new tab, applies safe `rel`, and * appends an external-link icon (unless `showExternalIcon` is `false`). */ external?: boolean; /** Show the external-link affordance icon for external links. */ showExternalIcon?: boolean; } & React.AnchorHTMLAttributes<HTMLAnchorElement> & VariantProps<typeof linkVariants>; /** * Styled anchor with selectable emphasis and an external-link affordance. * Composes onto custom routing components via `asChild`. * @example * <Link href="/docs">Read the docs</Link> * <Link href="https://example.com" external>Visit example</Link> */ const Link = ({ asChild = false, children, className, external = false, ref, rel, showExternalIcon = true, target, variant, ...props }: LinkProps & { ref?: React.Ref<HTMLAnchorElement> }) => { const Comp = asChild ? Slot : "a"; return ( <Comp className={cn(linkVariants({ variant }), className)} ref={ref} rel={external ? (rel ?? "noreferrer noopener") : rel} target={external ? (target ?? "_blank") : target} {...props} > {asChild ? ( children ) : ( <> {children} {external && showExternalIcon ? ( <ExternalLink aria-hidden="true" className="size-3.5 shrink-0" /> ) : null} </> )} </Comp> ); }; Link.displayName = "Link"; export { Link, linkVariants };

Dependencies

  • @vllnt/ui@^0.2.1
  • lucide-react