Skip to content
goodtabs UI
BrowseBrowse components

Arrow link

A plain text link with a small arrow that nudges right on hover and focus.

npx shadcn@latest add https://ui.goodtabs.co/r/arrow-link.json

With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/arrow-link

The plain Learn more link. Hover or focus to see the arrow move.

External

Opens in a new tab, with rel set and a note for screen readers.

In a sentence

Every component reads shadcn theme tokens only, so it matches your project the moment it lands. Read the docs

The sm size at the end of a grey paragraph.

Usage

import { ArrowLink } from "@/components/ui/arrow-link"

<ArrowLink href="/docs">Learn more</ArrowLink>
<ArrowLink href="https://github.com/acme" external>View on GitHub</ArrowLink>

Props

PropTypeDefaultDescription
hrefstring | UrlObjectWhere the link goes. Passed to next/link.
externalbooleanfalseOpens in a new tab with rel="noopener noreferrer" and a sr-only note.
size"sm" | "default""default"Text size: 12px with a 12px arrow, or 14px with a 14px arrow.
childrenReactNodeThe link text. Keep it short, two to four words.

Source

arrow-link/arrow-link.tsx
import * as React from "react";
import Link from "next/link";
import { ArrowRightIcon } from "lucide-react";
import { cn } from "@/lib/utils";

type ArrowLinkProps = React.ComponentProps<typeof Link> & {
  /** Open in a new tab with rel="noopener noreferrer" and a screen reader note. */
  external?: boolean;
  /** Text size. sm is 12px, default is 14px. */
  size?: "sm" | "default";
};

/**
 * A plain text link with a small arrow that nudges right on hover and focus.
 * Monochrome: the arrow is the hover signal, not a colour change.
 */
export function ArrowLink({ external = false, size = "default", className, children, ...props }: ArrowLinkProps) {
  return (
    <Link
      data-slot="arrow-link"
      target={external ? "_blank" : undefined}
      rel={external ? "noopener noreferrer" : undefined}
      className={cn(
        "group/arrow-link inline-flex items-center gap-1 rounded-sm font-medium text-foreground transition-colors duration-100 ease-out outline-none hover:text-foreground/85 focus-visible:ring-2 focus-visible:ring-ring/50",
        size === "sm" ? "text-xs" : "text-sm",
        className,
      )}
      {...props}
    >
      <span>{children}</span>
      <ArrowRightIcon
        aria-hidden="true"
        className={cn(
          "shrink-0 transition-transform duration-150 ease-out group-hover/arrow-link:translate-x-0.5 group-focus-visible/arrow-link:translate-x-0.5 motion-reduce:transition-none motion-reduce:group-hover/arrow-link:translate-x-0 motion-reduce:group-focus-visible/arrow-link:translate-x-0",
          size === "sm" ? "size-3" : "size-3.5",
        )}
      />
      {external && <span className="sr-only">(opens in a new tab)</span>}
    </Link>
  );
}

For AI agents

Hand your agent this page, the markdown at /components/arrow-link/prompt.md, or the Copy AI prompt button above.

When to use

  • The quiet second action next to a primary button in a hero or section header
  • A Learn more or See all link at the end of a feature, card or paragraph
  • Anywhere a link should read as a link without an underline

When not to use

  • As the only call to action on a page; use a solid Button for that
  • Inside long prose where several links sit in one paragraph; use plain underlined links
  • For actions that are not navigation, such as submit or delete

Agent notes

  • Server component built on next/link, so it needs a Next.js app and a real href; it accepts every Link prop
  • Monochrome by design: text stays foreground, the 2px arrow nudge is the hover and focus signal
  • The nudge is off under prefers-reduced-motion; the arrow still shows, so the link reads the same
  • external adds target, rel and a sr-only (opens in a new tab) note; the visible text does not change