Skip to main content

A styled anchor component for navigation with built-in icon support.

Import

import { Link } from "heroui-solid";

Usage

import { Link } from "heroui-solid"

export function LinkBasic() {
  return (
    <Link href="#">
      Call to action
      <Link.Icon />
    </Link>
  )
}

Anatomy

Import the Link component and access all parts using dot notation.

import { Link } from "heroui-solid";

export default () => (
  <Link href="#">
    Call to action
    <Link.Icon />
  </Link>
);

Custom Icon

import { ArrowUpRightFromSquare, Link as LinkIcon } from "gravity-icons-solid"
import { Link } from "heroui-solid"

export function LinkCustomIcon() {
  return (
    <div class="flex flex-col gap-3">
      <Link href="#">
        External link
        <Link.Icon class="ml-1.5 size-3">
          <ArrowUpRightFromSquare />
        </Link.Icon>
      </Link>
      <Link class="gap-1" href="#">
        Go to page
        <Link.Icon class="size-3">
          <LinkIcon />
        </Link.Icon>
      </Link>
    </div>
  )
}

Icon Placement

import { Link } from "heroui-solid"

export function LinkIconPlacement() {
  return (
    <div class="flex flex-col gap-3">
      <Link href="#">
        Icon at end (default)
        <Link.Icon />
      </Link>
      <Link class="gap-1" href="#">
        <Link.Icon />
        Icon at start
      </Link>
    </div>
  )
}

Accessibility: The default external-link icon is decorative (aria-hidden), so screen readers don't announce it. When a link opens in a new tab, convey that in the link text or an aria-label, e.g. aria-label="Creator Hub (opens in new tab)".

Text Decoration with Tailwind CSS

Link is underlined on hover by default. Use Tailwind CSS text-decoration utilities to make the underline always visible, remove it entirely, or customize its color, style, thickness, and offset.

import { Link } from "heroui-solid"

export function LinkUnderlineAndOffset() {
  return (
    <div class="flex flex-col gap-6">
      <div class="flex flex-col gap-2">
        <p class="text-sm font-medium text-muted">Default hover underline</p>
        <Link href="#">
          Hover to see the underline
          <Link.Icon />
        </Link>
      </div>

      <div class="flex flex-col gap-2">
        <p class="text-sm font-medium text-muted">Always visible underline</p>
        <Link class="underline" href="#">
          Underline always visible
          <Link.Icon />
        </Link>
      </div>

      <div class="flex flex-col gap-2">
        <p class="text-sm font-medium text-muted">No underline</p>
        <Link class="no-underline" href="#">
          Link without any underline
          <Link.Icon />
        </Link>
      </div>

      <div class="flex flex-col gap-2">
        <p class="text-sm font-medium text-muted">
          Changing the underline offset
        </p>
        <div class="flex flex-col gap-3">
          <Link class="underline-offset-1" href="#">
            Offset 1 (1px space)
            <Link.Icon />
          </Link>
          <Link class="underline-offset-2" href="#">
            Offset 2 (2px space)
            <Link.Icon />
          </Link>
          <Link class="underline-offset-3" href="#">
            Offset 3 (3px space)
            <Link.Icon />
          </Link>
          <Link class="underline-offset-4" href="#">
            Offset 4 (4px space)
            <Link.Icon />
          </Link>
        </div>
      </div>
    </div>
  )
}

Text Decoration Line:

  • underline - Always visible underline
  • no-underline - Remove underline
  • default Link styles - Underline appears on hover

Text Decoration Color:

  • decoration-accent, decoration-muted, etc. - Set underline color using theme colors
  • decoration-muted/50 - Use opacity modifiers for semi-transparent underlines

Text Decoration Style:

  • decoration-solid - Solid line (default)
  • decoration-double - Double line
  • decoration-dotted - Dotted line
  • decoration-dashed - Dashed line
  • decoration-wavy - Wavy line

Text Decoration Thickness:

  • decoration-1, decoration-2, decoration-4, etc. - Control underline thickness

Underline Offset:

  • underline-offset-1, underline-offset-2, underline-offset-4, etc. - Adjust spacing between text and underline

For more details, see the Tailwind CSS documentation:

Custom Element

HeroUI React overrides the rendered element with a render prop. This port uses Kobalte's polymorphic as prop instead — for example, a button element that looks like a link:

import { Link } from "heroui-solid"

export function LinkCustomElement() {
  return (
    <Link as="button" onClick={() => alert("Link activated!")} type="button">
      Link-styled button
      <Link.Icon />
    </Link>
  )
}
  • Breadcrumbs: Display the user's current location within a hierarchy (not ported yet)

Styling

Passing Tailwind CSS classes

import { Link } from "heroui-solid";

function CustomLink() {
  return (
    <Link
      href="#"
      class="text-lg font-bold text-accent hover:text-accent/80"
    >
      Custom styled link
    </Link>
  );
}

Customizing the component classes

To customize the Link component classes, you can use the @layer components directive. Learn more.

@layer components {
  .link {
    @apply font-semibold;
  }
}

HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

The Link component uses these CSS classes (View source styles):

Base Classes

  • .link - Base link styles
  • .link__icon - Link icon styles

Interactive States

The component supports CSS pseudo-classes and data attributes:

  • Focus: :focus-visible (shows focus ring)
  • Hover: :hover
  • Pressed: :active
  • Disabled: [aria-disabled="true"] (reduced opacity, no pointer events)

API Reference

PropTypeDefaultDescription
hrefstring-Destination URL for the anchor
targetstring"_self"Controls where to open the linked document
relstring-Relationship between the current and linked documents
downloadboolean | string-Prompts file download instead of navigation
isDisabledbooleanfalseDisables pointer and keyboard interaction
classstring-Custom classes merged with the default styles
childrenJSX.Element-Content rendered inside the link
onClickJSX.EventHandlerUnion<HTMLElement, MouseEvent>-Handler called when the link is clicked
asValidComponent"a"Overrides the default DOM element (Kobalte polymorphic)

Link.Icon Props

PropTypeDefaultDescription
childrenJSX.Element-Custom icon element; defaults to the built-in arrow icon when omitted
classstring-Additional CSS classes

Using with Routing Libraries

With SolidStart, @solidjs/router intercepts native <a> elements for client-side navigation, so Link works out of the box — just pass href:

import { Link } from "heroui-solid";

export default function Demo() {
  return (
    <Link href="/about">
      About Page
      <Link.Icon />
    </Link>
  );
}

For routers that ship their own link component, like TanStack Router, use variant functions to style it:

import { Link as RouterLink } from "@tanstack/solid-router";
import { Link, linkVariants } from "heroui-solid";

export default function Demo() {
  const slots = linkVariants();

  return (
    <RouterLink class={slots.base()} to="/about">
      About Page
      <Link.Icon class={slots.icon()} />
    </RouterLink>
  );
}

Direct Class Application

Since HeroUI uses BEM classes, you can apply Link styles directly to any link element:

import { Link as RouterLink } from "@tanstack/solid-router";

// Apply classes directly with Tailwind utilities
export default function Demo() {
  return (
    <RouterLink class="link underline-offset-2" to="/about">
      About Page
    </RouterLink>
  );
}

// Or with a native anchor
export default function NativeLink() {
  return (
    <a class="link underline decoration-accent underline-offset-4" href="/about">
      About Page
      <Link.Icon class="link__icon" />
    </a>
  );
}

Differences from HeroUI React

  • Use class instead of className; the element type is overridden with Kobalte's polymorphic as prop instead of React's render prop.
  • Use onClick (Solid's native event) instead of React Aria's onPress.
  • children is plain JSX — the React render-prop form (receiving isHovered, isPressed, etc.) isn't supported.
  • Hover/press styling relies on HeroUI's native :hover / :active CSS fallbacks rather than React Aria's data-hovered / data-pressed attributes — visuals are identical.
  • The routing example uses TanStack Router instead of Next.js — with SolidStart's own router, native <a> interception means Link needs no adapter at all.

Last updated: 7/19/26, 3:27 AM

HeroUI SolidUnofficial SolidJS port of HeroUI v3, built on Kobalte and @heroui/styles