Link
linkuiStyled anchor with router integration. Renders an <a> for href, a router-link for to, and handles external links with target/rel. Supports underline variants (always/hover/none), color variants (default/primary/muted), disabled state, left/right icon slots, size variants, and asChild composition.
Installation
$pnpm dlx uipkge-ng@latest add link$npx uipkge-ng@latest add link$yarn dlx uipkge-ng@latest add link$bunx uipkge-ng@latest add linkManifest:
https://uipkge.dev/r/angular/link.jsonInstalls to:src/app/components/ui/link/Examples
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
underline | 'none''always''hover' | hover | optional |
color | 'default''primary''muted' | primary | optional |
size | 'sm''default''lg' | default | optional |
hrefExternal URL — renders an anchor with target/rel handling. | string | — | optional |
toRouter destination (string); wins over `href`, like React. | string | object | — | optional |
disabledDisabled links render without href/navigation and get aria-disabled. | boolean | false | optional |
externalOpen external href in a new tab. Defaults to true for http(s) hrefs. | boolean | — | optional |
class | string | — | optional |
npm dependencies
Files installed (3)
src/app/components/ui/link/link.component.ts3.9 kB
import { Component, ElementRef, Input, booleanAttribute, inject, ChangeDetectionStrategy } from '@angular/core' import { cn } from '@/lib/utils' import { linkVariants, type LinkVariants } from './link.variants' export type LinkUnderline = NonNullable<LinkVariants['underline']> export type LinkColor = NonNullable<LinkVariants['color']> export type LinkSize = NonNullable<LinkVariants['size']> /** * Angular port of UIPKGE Link (React `Link`, an `<a>` by default). Put it on the element you * want: `<a ui-link href="...">` is the React default; any other element (`<button ui-link>`) * is the Radix `asChild` form and still gets the link styling. http(s) hrefs open in a new tab * with rel=noopener unless `external` says otherwise; disabled links drop href, get * aria-disabled + tabindex -1 and swallow clicks (and are never external). `left` / `right` * icon slots project via `[slot=left]` / `[slot=right]`. The `<ui-link>` custom element gets * link semantics (role="link", focusable, Enter / click navigate to the href). */ @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-link, [ui-link]', standalone: true, host: { '[attr.data-uipkge]': '""', '[attr.data-slot]': '"link"', '[attr.data-underline]': 'underline ?? null', '[attr.data-color]': 'color ?? null', '[attr.data-size]': 'size ?? null', '[attr.data-disabled]': 'disabled ? "" : null', '[attr.href]': 'resolvedHref', '[attr.aria-disabled]': 'disabled ? "true" : null', '[attr.tabindex]': 'disabled ? -1 : isCustom ? 0 : null', '[attr.role]': 'isCustom ? "link" : null', '[attr.target]': 'isExternal ? "_blank" : null', '[attr.rel]': 'isExternal ? "noopener noreferrer" : null', '[class]': 'hostClass', '(click)': 'onClick($event)', '(keydown.enter)': 'onEnter($event)', }, template: `<ng-content select="[slot=left]" /><ng-content /><ng-content select="[slot=right]" />`, }) export class UiLinkComponent { private readonly el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement /** `<ui-link>` custom element: not an anchor, so it needs role / focus / navigation. */ readonly isCustom = this.el.tagName === 'UI-LINK' /** External URL — renders an anchor with target/rel handling. */ @Input() href?: string /** Router destination (string); wins over `href`, like React. */ @Input() to?: string | object @Input() underline?: LinkUnderline @Input() color?: LinkColor @Input() size?: LinkSize /** Disabled links render without href/navigation and get aria-disabled. */ @Input({ transform: booleanAttribute }) disabled = false /** Open external href in a new tab. Defaults to true for http(s) hrefs. */ @Input({ transform: booleanAttribute }) external?: boolean @Input('class') className?: string get isExternal(): boolean { if (this.disabled) return false if (this.external !== undefined) return this.external return typeof this.href === 'string' && /^https?:\/\//.test(this.href) } get resolvedHref(): string | null { if (this.disabled) return null if (typeof this.to === 'string') return this.to return this.href ?? null } get hostClass(): string { return cn( linkVariants({ underline: this.underline, color: this.color, size: this.size }), this.disabled && 'pointer-events-none opacity-50', this.className, ) } onClick(event: MouseEvent): void { if (this.disabled) { event.preventDefault() event.stopPropagation() return } if (this.isCustom && this.resolvedHref) this.navigate() } onEnter(event: Event): void { if (!this.isCustom || this.disabled) return event.preventDefault() this.el.click() } private navigate(): void { const href = this.resolvedHref! if (this.isExternal) window.open(href, '_blank', 'noopener,noreferrer') else window.location.assign(href) } } export { linkVariants, type LinkVariants }src/app/components/ui/link/link.variants.ts1 kB
import type { VariantProps } from 'class-variance-authority' import { cva } from 'class-variance-authority' export const linkVariants = cva( "inline-flex items-center gap-1.5 font-medium transition-colors duration-200 outline-none focus-visible:ring-ring/50 focus-visible:ring-[3px] rounded-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", { variants: { underline: { none: 'no-underline', always: 'underline underline-offset-4', hover: 'no-underline hover:underline hover:underline-offset-4', }, color: { default: 'text-foreground hover:text-foreground/80', primary: 'text-primary hover:text-primary/80', muted: 'text-muted-foreground hover:text-foreground', }, size: { sm: 'text-xs gap-1', default: 'text-sm', lg: 'text-base', }, }, defaultVariants: { underline: 'hover', color: 'primary', size: 'default', }, }, ) export type LinkVariants = VariantProps<typeof linkVariants>src/app/components/ui/link/index.ts0.1 kB
export { UiLinkComponent, linkVariants, type LinkVariants, type LinkUnderline, type LinkColor, type LinkSize, } from './link.component'
Raw manifest:https://uipkge.dev/r/angular/link.json