UIPackage
Menu

Link

link ui
Edit on GitHub

Styled 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.

Also available for React ->

Installation

$ npx shadcn-vue@latest add https://uipkge.dev/r/vue/link.json
Named registry: npx shadcn-vue@latest add @uipkge/link Installs to: app/components/ui/link/

Examples

Props

Name Type / Values Default Required
href

External URL — renders an <a> with target/rel handling.

string optional
to

Router destination — renders a router-link when vue-router is present.

string | object optional
as string 'a' optional
asChild boolean optional
underline Underline 'hover' optional
color Color 'primary' optional
size Size 'default' optional
disabled boolean optional
external

Open external href in a new tab. Defaults to true for http(s) hrefs.

boolean undefined optional
class HTMLAttributes['class'] optional

Files installed (3)

  • app/components/ui/link/Link.vue 2 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { computed } from 'vue'
    import { Primitive } from 'reka-ui'
    import { cn } from '@/lib/utils'
    import { linkVariants } from './link.variants'
    
    type Underline = 'none' | 'always' | 'hover'
    type Color = 'default' | 'primary' | 'muted'
    type Size = 'sm' | 'default' | 'lg'
    
    interface Props {
      /** External URL — renders an <a> with target/rel handling. */
      href?: string
      /** Router destination — renders a router-link when vue-router is present. */
      to?: string | object
      as?: string
      asChild?: boolean
      underline?: Underline
      color?: Color
      size?: Size
      disabled?: boolean
      /** Open external href in a new tab. Defaults to true for http(s) hrefs. */
      external?: boolean
      class?: HTMLAttributes['class']
    }
    
    const props = withDefaults(defineProps<Props>(), {
      as: 'a',
      underline: 'hover',
      color: 'primary',
      size: 'default',
      external: undefined,
    })
    
    const isExternal = computed(() => {
      if (props.external !== undefined) return props.external
      return typeof props.href === 'string' && /^https?:\/\//.test(props.href)
    })
    
    const resolvedTag = computed(() => {
      if (props.asChild) return Primitive
      if (props.to) return 'router-link'
      return props.as
    })
    
    const resolvedHref = computed(() => props.to ?? props.href)
    
    const externalAttrs = computed(() => (isExternal.value ? { target: '_blank', rel: 'noopener noreferrer' } : {}))
    </script>
    
    <template>
      <component
        :is="resolvedTag"
        data-uipkge
        data-slot="link"
        :data-underline="underline"
        :data-color="color"
        :data-size="size"
        :data-disabled="disabled ? '' : undefined"
        :as="asChild ? as : undefined"
        :as-child="asChild"
        :to="to"
        :href="resolvedHref"
        :aria-disabled="disabled ? 'true' : undefined"
        :tabindex="disabled ? -1 : undefined"
        v-bind="externalAttrs"
        :class="cn(linkVariants({ underline, color, size }), disabled && 'pointer-events-none opacity-50', props.class)"
      >
        <slot name="left" />
        <slot />
        <slot name="right" />
      </component>
    </template>
  • app/components/ui/link/link.variants.ts 1 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>
  • app/components/ui/link/index.ts 0.1 kB
    export { default as Link } from './Link.vue'
    
    export { linkVariants, type LinkVariants } from './link.variants'

Raw manifest: https://uipkge.dev/r/vue/link.json