Building Type-Safe APIs with Nuxt 3 and Prisma
Learn how to build end-to-end type-safe APIs using Nuxt 3 server routes and Prisma ORM for a seamless developer experience.
Create a reusable, themeable component library using Vue 3, Tailwind CSS, and proper documentation with Storybook.
Prince Mbeah Essilfie
May 2, 2026 at 06:04 AM
Ready-made libraries are great for prototyping. But for production products, a custom design system gives you:
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
<!-- 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>
// 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)',
},
}
--color-primary-500 to their brand color.{
"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>
Without docs, nobody will use your design system. At minimum:
A design system is a product. Treat it like one.
Fullstack Software Developer
Learn how to build end-to-end type-safe APIs using Nuxt 3 server routes and Prisma ORM for a seamless developer experience.
Go beyond basic utility classes and learn how to build cohesive, maintainable design systems with Tailwind CSS.
Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.
