UIPackage
Menu

Framework

Change language

Boilerplate repo

Chip

chipui

Compact, removable tag — typically used inside `tags-input` or as a filter pill. Seven variants (`default`, `filled`, `outlined`, `elevated`, `success`, `warning`, `destructive`), three sizes, and an optional close button.

Also available for React ->

Installation

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

Examples

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
variant
'default''filled''outlined''outline''elevated''success''warning''destructive'
optional
size
'sm''default''lg'
optional
wrap

Allow the label to wrap onto multiple lines instead of clipping.

booleanoptional
classHTMLAttributes['class']optional
closablebooleanoptional

Used by

Files installed (4)

  • app/components/ui/chip/Chip.vue2.2 kB
    <script setup lang="ts">
    import { ref } from 'vue'
    import type { HTMLAttributes } from 'vue'
    import { X } from 'lucide-vue-next'
    import { cn } from '@/lib/utils'
    import { chipVariants } from './chip.variants'
    
    // Inlined unions: SFC compiler can't extract runtime props from
    // `ChipVariants['variant'] | ['size']`.
    const props = defineProps<{
      variant?: 'default' | 'filled' | 'outlined' | 'outline' | 'elevated' | 'success' | 'warning' | 'destructive'
      size?: 'sm' | 'default' | 'lg'
      /** Allow the label to wrap onto multiple lines instead of clipping. */
      wrap?: boolean
      class?: HTMLAttributes['class']
      closable?: boolean
    }>()
    
    const emit = defineEmits<{
      close: []
    }>()
    
    const leaving = ref(false)
    
    function onClose() {
      if (leaving.value) return
      leaving.value = true
      const reduce = typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches
      window.setTimeout(
        () => {
          emit('close')
        },
        reduce ? 0 : 160,
      )
    }
    </script>
    
    <template>
      <span
        data-uipkge
        data-slot="chip"
        :data-leaving="leaving || undefined"
        :class="cn(chipVariants({ variant, size, wrap }), 'chip-enter', leaving && 'chip-leave', props.class)"
      >
        <slot />
        <button
          v-if="closable"
          type="button"
          aria-label="Remove item"
          class="focus-visible:ring-ring hover:bg-foreground/10 ml-1 inline-flex min-h-6 min-w-6 items-center justify-center rounded-full transition-transform duration-150 focus-visible:ring-1 focus-visible:outline-none active:scale-90"
          @click.stop="onClose"
        >
          <X class="size-3" aria-hidden="true" />
        </button>
      </span>
    </template>
    
    <style>
    @keyframes chip-enter {
      from {
        opacity: 0;
        transform: scale(0.88);
      }
      to {
        opacity: 1;
        transform: scale(1);
      }
    }
    
    @keyframes chip-leave {
      to {
        opacity: 0;
        transform: scale(0.88);
      }
    }
    
    [data-slot='chip'].chip-enter {
      animation: chip-enter 180ms cubic-bezier(0.22, 1.2, 0.36, 1) both;
    }
    
    [data-slot='chip'].chip-leave {
      animation: chip-leave 160ms ease-in both;
      pointer-events: none;
    }
    
    @media (prefers-reduced-motion: reduce) {
      [data-slot='chip'].chip-enter,
      [data-slot='chip'].chip-leave {
        animation: none !important;
      }
    }
    </style>
    
  • app/components/ui/chip/ChipGroup.vue1.5 kB
  • app/components/ui/chip/chip.variants.ts2.1 kB
  • app/components/ui/chip/index.ts0.3 kB

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