Skip to content
    Style: Wireframe

    Components · Actions

    Link

    Inline in text, standalone with an arrow, or external with an arrow out and a new-tab note for screen readers.

    3.7% of the 2,073 sites in The UI Index have a link (77 sites).

    Used in UI Pro: Footer, Header, Navigation, Breadcrumb

    Deploys run on every push. Read how previews work before you invite your team.

    See all integrationsStatus page (opens in a new tab)

    Both previews render the same markup; the HTML tab below is that markup without a framework.

    Install

    React
    npx shadcn@latest add https://www.themes.dev/ui/r/link.json
    Astro
    npx shadcn@latest add https://www.themes.dev/ui/r/astro/link.json

    Or copy the files below into your project. They need clsx, tailwind-merge, and the themes.dev UI theme. The HTML needs only the compiled CSS.

    link.tsx
    import type { AnchorHTMLAttributes } from 'react';
    import { cn } from '@/lib/utils';
    import { ARROW_PATH, EXTERNAL_PATH, linkVariants, type LinkVariants } from './link.variants';
    
    export type LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & LinkVariants & { href: string; newTab?: boolean };
    
    /** A link: inline in text, standalone with an arrow, or external with an arrow out. */
    export function Link({ variant, href, newTab, className, children, ...props }: LinkProps) {
      const icon = variant === 'standalone' ? ARROW_PATH : variant === 'external' ? EXTERNAL_PATH : null;
      return (
        <a href={href} target={newTab ? '_blank' : undefined} rel={newTab ? 'noopener' : undefined} className={cn(linkVariants({ variant }), className)} {...props}>
          {children}
          {icon && <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d={icon} /></svg>}
          {newTab && <span className="sr-only"> (opens in a new tab)</span>}
        </a>
      );
    }
    export { linkVariants };
    link.variants.ts
    import { cva, type VariantProps } from 'class-variance-authority';
    
    /** Links. inline: in running text, underlined (the underline darkens on hover and thickens when pressed).
     *  standalone: on its own line, with an arrow that steps forward on hover. external: opens elsewhere, marked with
     *  an arrow out and "(opens in a new tab)" for screen readers when it does. */
    export const linkVariants = cva(
      'rounded-sm font-medium text-foreground underline-offset-4 transition-[color,text-decoration-color,background-color] active:bg-accent [&_svg]:size-3.5 [&_svg]:shrink-0 [&_svg]:transition-transform',
      {
        variants: {
          variant: {
            inline: 'underline decoration-foreground/30 hover:decoration-foreground active:decoration-2',
            standalone: 'group inline-flex min-h-6 items-center gap-1.5 hover:underline active:decoration-2 hover:[&_svg]:translate-x-0.5',
            external: 'inline-flex min-h-6 items-center gap-1 underline decoration-foreground/30 hover:decoration-foreground active:decoration-2 hover:[&_svg]:-translate-y-px hover:[&_svg]:translate-x-px',
          },
        },
        defaultVariants: { variant: 'inline' },
      },
    );
    export type LinkVariants = VariantProps<typeof linkVariants>;
    export const ARROW_PATH = 'M3 8h10M9 4l4 4-4 4';
    export const EXTERNAL_PATH = 'M5 11l6-6M6 5h5v5';