Menu

Change language

GitHub repo

Link

linkui

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.

Installation

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

Examples

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
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
asstring'a'optional
asChildboolean—optional
underline
'none''always''hover'
'hover'optional
color
'default''primary''muted'
'primary'optional
size
'sm''default''lg'
'default'optional
disabledboolean—optional
external

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

booleanundefinedoptional
classHTMLAttributes['class']—optional

Files installed (3)

  • app/components/ui/link/Link.vue2.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="disabled ? undefined : to"
        :href="disabled ? undefined : resolvedHref"
        :aria-disabled="disabled ? 'true' : undefined"
        :tabindex="disabled ? -1 : undefined"
        v-bind="disabled ? {} : externalAttrs"
        :class="cn(linkVariants({ underline, color, size }), disabled && 'pointer-events-none opacity-50', props.class)"
        @click="
          (e: MouseEvent) => {
            if (disabled) {
              e.preventDefault()
              e.stopPropagation()
            }
          }
        "
      >
        <slot name="left" />
        <slot />
        <slot name="right" />
      </component>
    </template>
    
  • app/components/ui/link/link.variants.ts1 kB
  • app/components/ui/link/index.ts0.1 kB

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