Skip to main content
Web DevelopmentTutorials

Building a Design System Component Library with Vue and Tailwind

Create a reusable, themeable component library using Vue 3, Tailwind CSS, and proper documentation with Storybook.

Prince Mbeah Essilfie

Prince Mbeah Essilfie

May 2, 2026 at 06:04 AM

10 min read 306 views
Building a Design System Component Library with Vue and Tailwind

Why Build Your Own?

Ready-made libraries are great for prototyping. But for production products, a custom design system gives you:

  • Exact match to your brand
  • Only the components you need (smaller bundle)
  • Full control over behavior and accessibility
  • Consistent patterns across all products

Project Structure

design-system/
├── src/
│   ├── components/
│   │   ├── Button/
│   │   │   ├── Button.vue
│   │   │   ├── Button.test.ts
│   │   │   ├── Button.stories.ts
│   │   │   └── index.ts
│   │   ├── Input/
│   │   └── Modal/
│   ├── composables/
│   ├── tokens/
│   │   └── colors.ts
│   └── index.ts          ← Public API
├── tailwind.config.ts
└── package.json

The Button Component

<!-- components/Button/Button.vue -->
<template>
  <component
    :is="tag"
    :class="buttonClasses"
    :disabled="disabled || loading"
    v-bind="$attrs"
  >
    <span v-if="loading" class="animate-spin mr-2">
      <IconLoader class="w-4 h-4" />
    </span>
    <span v-if="$slots.leading" class="mr-2">
      <slot name="leading" />
    </span>
    <slot />
    <span v-if="$slots.trailing" class="ml-2">
      <slot name="trailing" />
    </span>
  </component>
</template>

<script setup lang="ts">
export interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'ghost' | 'danger'
  size?: 'xs' | 'sm' | 'md' | 'lg'
  loading?: boolean
  disabled?: boolean
  tag?: string | Component
}

const props = withDefaults(defineProps<ButtonProps>(), {
  variant: 'primary',
  size: 'md',
  tag: 'button',
})

const buttonClasses = computed(() => [
  'inline-flex items-center justify-center font-medium rounded-lg',
  'transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2',
  'disabled:opacity-50 disabled:cursor-not-allowed',
  variantClasses[props.variant],
  sizeClasses[props.size],
])

const variantClasses = {
  primary: 'bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500',
  secondary: 'border border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-gray-500',
  ghost: 'text-gray-700 hover:bg-gray-100 focus:ring-gray-500',
  danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',
}

const sizeClasses = {
  xs: 'px-2 py-1 text-xs',
  sm: 'px-3 py-1.5 text-sm',
  md: 'px-4 py-2 text-sm',
  lg: 'px-5 py-2.5 text-base',
}
</script>

Token-Based Theming

// tokens/colors.ts
export const colors = {
  primary: {
    50: 'var(--color-primary-50, #eff6ff)',
    500: 'var(--color-primary-500, #3b82f6)',
    600: 'var(--color-primary-600, #2563eb)',
    700: 'var(--color-primary-700, #1d4ed8)',
  },
}
CSS custom properties as the theming layer means consumers can override your design tokens without rebuilding the library. Just set --color-primary-500 to their brand color.

Publishing as a Package

{
  "name": "@myorg/design-system",
  "exports": {
    ".": "./src/index.ts",
    "./tailwind": "./tailwind.config.ts"
  },
  "peerDependencies": {
    "vue": "^3.4.0",
    "tailwindcss": "^4.0.0"
  }
}

Consumer usage:

// Consumer's tailwind.config.ts
import designSystem from '@myorg/design-system/tailwind'

export default {
  presets: [designSystem],
}
<script setup>
import { Button, Input, Modal } from '@myorg/design-system'
</script>

Documentation Is Critical

Without docs, nobody will use your design system. At minimum:

  • Props table for each component
  • Visual examples of all variants
  • Copy-pasteable code snippets
  • Accessibility notes

A design system is a product. Treat it like one.

TypeScriptVueCSSTailwind CSS
Prince Mbeah Essilfie

Written by Prince Mbeah Essilfie

Fullstack Software Developer

Read next

The Complete Guide to Vue 3 Composables
12 min read

The Complete Guide to Vue 3 Composables

Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.

Prince Mbeah EssilfiePrince Mbeah Essilfie
1.1K