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 anaria-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.
Default hover underline
Hover to see the underlineAlways visible underline
Underline always visibleNo underline
Link without any underlineChanging the underline 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 underlineno-underline- Remove underline- default
Linkstyles - Underline appears on hover
Text Decoration Color:
decoration-accent,decoration-muted, etc. - Set underline color using theme colorsdecoration-muted/50- Use opacity modifiers for semi-transparent underlines
Text Decoration Style:
decoration-solid- Solid line (default)decoration-double- Double linedecoration-dotted- Dotted linedecoration-dashed- Dashed linedecoration-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:
- text-decoration-line
- text-decoration-color
- text-decoration-style
- text-decoration-thickness
- text-underline-offset
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>
)
}Related Components
- 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
Link Props
| Prop | Type | Default | Description |
|---|---|---|---|
href | string | - | Destination URL for the anchor |
target | string | "_self" | Controls where to open the linked document |
rel | string | - | Relationship between the current and linked documents |
download | boolean | string | - | Prompts file download instead of navigation |
isDisabled | boolean | false | Disables pointer and keyboard interaction |
class | string | - | Custom classes merged with the default styles |
children | JSX.Element | - | Content rendered inside the link |
onClick | JSX.EventHandlerUnion<HTMLElement, MouseEvent> | - | Handler called when the link is clicked |
as | ValidComponent | "a" | Overrides the default DOM element (Kobalte polymorphic) |
Link.Icon Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | JSX.Element | - | Custom icon element; defaults to the built-in arrow icon when omitted |
class | string | - | 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
classinstead ofclassName; the element type is overridden with Kobalte's polymorphicasprop instead of React'srenderprop. - Use
onClick(Solid's native event) instead of React Aria'sonPress. childrenis plain JSX — the React render-prop form (receivingisHovered,isPressed, etc.) isn't supported.- Hover/press styling relies on HeroUI's native
:hover/:activeCSS fallbacks rather than React Aria'sdata-hovered/data-pressedattributes — visuals are identical. - The routing example uses TanStack Router instead of Next.js — with SolidStart's own
router, native
<a>interception meansLinkneeds no adapter at all.
Last updated: 7/19/26, 3:27 AM