Link
Navigate or open external resources.
use herogpui::components::link::Link;Link on docs.rsSource at v0.14.0
Usage
Showing Usage, live: HeroGPUI compiled to WebAssembly. The canvas has no accessibility tree; each example's description and code below carry the same information.
`radius(px)` replaces the root's default `rounded-xl`; the link paints no fill of its own, so this one carries an `sx` background to show the corner. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::Link;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![Link::new("link-hover")
.label("Hover to see the underline")
.href("#")
.radius(px(4.))
.sx(|el| el.bg(gpui::rgba(0x22c55e33)))
.into_any_element()])An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ActiveTheme, Link, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
Link::new("link-icon-end")
.label("Icon at end (default)")
.icon(gpui::svg()
.size(px(16.))
.path(icons::EXTERNAL_LINK)
.text_color(cx.colors().foreground)
.into_any_element())
.href("#")
.into_any_element(),
Link::new("link-icon-start")
.label("Icon at start")
.icon(gpui::svg()
.size(px(16.))
.path(icons::EXTERNAL_LINK)
.text_color(cx.colors().foreground)
.into_any_element())
.icon_first(true)
.href("#")
.into_any_element(),
])The pinned `.link` carries `no-underline decoration-[1.5px]`; hover recolours the decoration to `decoration-muted/50` and press to `decoration-muted`. The text colour itself never changes state; a different decoration is the caller's own styling on the element they own. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::Link;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
Link::new("link-decor")
.label("Underlined on hover")
.href("#")
.into_any_element(),
])An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ActiveTheme, Link, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![Link::new("link-custom-icon")
.label("Open the docs")
.icon(gpui::svg()
.size(px(16.))
.path(icons::ARROW_RIGHT)
.text_color(cx.colors().foreground)
.into_any_element())
.href("#")
.into_any_element()])The `render` closure hands the link's interactive state to a caller-built element. The root keeps the `href`, press, focus and disabled wiring, and the closure draws the content from the state alone. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::Link;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
Link::new("link-render")
.href("#")
.render(|state| {
gpui::div()
.flex()
.items_center()
.gap(px(6.))
.child("Call to action")
.child(gpui::div().text_size(px(12.)).opacity(0.6).child(
if state.is_hovered {
"hovered"
} else if state.is_pressed {
"pressed"
} else if state.is_focus_visible {
"focus-visible"
} else if state.is_focused {
"focused"
} else if state.is_disabled {
"disabled"
} else {
"custom render"
},
))
.into_any_element()
})
.into_any_element(),
])Anatomy
Rust types
Link is assembled from these builders. The API reference lists each one.
API reference
Against HeroUI v3.2.6: 6 entries ported, 7 partial, 5 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.
Builders
| Builder | Values | Default | Description |
|---|---|---|---|
href(Partial | string | — | Destination for the anchor. GPUI has no in-app navigation graph, so `href` opens through the OS handler (`App::open_url`) on press. |
is_disabled( | true | | false | Disables pointer and keyboard interaction: the disabled opacity, no pointer reach, and no tab stop. |
label(Partial | AnyElement | — | Content rendered inside the link. Composes it as the label text plus an optional icon element, ordered by where the icon belongs (orders `Link.Icon` among the children). |
on_press(Partial | Fn( | — | Fired when the link is activated, in addition to opening `href`. The callback receives GPUI's ClickEvent. |
auto_focus( | true | | — | Whether the element should receive focus on render. A link is not otherwise a focus target here, so this also makes it one; a disabled link is skipped by Tab and must not grab focus either. |
render(Partial | render closure< | — | Overrides the default element with a custom render function. GPUI has no props to spread onto a caller-built element, so the closure receives the interactive half alone (`isHovered`, `isPressed`, `isFocused`, `isFocusVisible`, `isDisabled`) and draws the content; the root keeps the `href`, `onPress`, focus and disabled wiring either way. |
icon(Partial | AnyElement | — | Custom icon content. A childless `Link.Icon` renders v3's built-in arrow with `data-default-icon="true"` and its `ms-1 pb-1.5` spacing, and that default arrow has no port path: `icon(element)` is the custom-children path only, so draws no icon at all without it. `icon_first(true)` moves the icon before the label the way v3's child ordering does. |
Not ported5 HeroUI entries not ported
| HeroUI v3 | Why it is not ported |
|---|---|
Link.target | Where to open the linked document. Has no browser navigation or HTTP transport — `href` opens through the OS handler — so there is nothing for a target to control. |
Link.rel | Relationship between the documents. Meaningless without an anchor element handed to a browser; the OS handler receives only the URL string. |
Link.download | Prompts a file download. The OS handler decides what to do with the URL; has no HTTP transport to fetch and save it. |
Link.className | Custom classes merged with the default styles. |
Link.Icon.className |
Parts
| Part | Description |
|---|---|
LinkPartial | Root anchor on the `.link` BEM class: `relative inline-flex h-fit w-fit items-center rounded-xl font-medium text-link no-underline decoration-[1.5px]`. The text colour is the `--link` token and never changes state; is a block-level flex row (no inline-flex) that hugs its content. |
LinkPartial | `hit testing-none inline-flex size-[0.75em] shrink-0 items-center justify-center text-current opacity-60`, raised to full opacity on hover, press and focus. `[data-default-icon=true]` adds `ms-1 pb-1.5`, but that rule belongs only to the childless built-in arrow, which does not draw; `icon(element)` is the arbitrary-children path and gets no default-icon spacing. Draws the caller's element verbatim, flush against the label, without the em-sized box or the default 60% opacity. |
States
| State | Builder | Description |
|---|---|---|
Hovered | hover(StyleRefinement underline) with colors.muted.alpha(0.5) | Turns the 1.5px underline on with `decoration-muted/50`; the text colour never changes. |
Pressed | active(StyleRefinement underline) with colors.muted | Takes the underline decoration to full `decoration-muted`. |
Focus visible | tab_stop_handle | Keyboard focus displays the status-focused ring; `track_focus` is what puts the link in the tab order. |
Disabled | is_disabled | Status-disabled: the disabled opacity, no pointer reach, and no tab stop. |