How to use Tailwind CSS in Vue
Using Tailwind CSS with Vue enables rapid development with utility-first styling, providing pre-built classes for responsive design, spacing, colors, and component styling. As the creator of CoreUI, a widely used open-source UI library, I’ve integrated Tailwind CSS with Vue applications across numerous enterprise projects for rapid prototyping, custom design systems, and utility-based styling approaches. From my expertise, the most effective approach is to install Tailwind CSS with PostCSS configuration. This method provides full utility access, purging for optimized builds, and seamless integration with Vue’s styling workflow while maintaining component-scoped styles and development efficiency.
Install Tailwind CSS with PostCSS and configure it for Vue projects with utility-first styling approach.
# Install Tailwind CSS and dependencies
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
# This creates tailwind.config.js and postcss.config.js
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
}
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
}
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography')
],
}
/* src/assets/css/tailwind.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom component classes */
@layer components {
.btn {
@apply px-4 py-2 rounded-md font-medium transition-colors;
}
.btn-primary {
@apply bg-primary-500 text-white hover:bg-primary-600;
}
.card {
@apply bg-white rounded-lg shadow-md border border-gray-200;
}
}
<template>
<div class="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div class="max-w-md mx-auto bg-white rounded-lg shadow-md overflow-hidden">
<!-- Header -->
<div class="bg-gradient-to-r from-blue-500 to-purple-600 px-6 py-4">
<h1 class="text-white text-xl font-bold">{{ title }}</h1>
<p class="text-blue-100 text-sm">{{ subtitle }}</p>
</div>
<!-- Content -->
<div class="p-6 space-y-4">
<div class="flex items-center space-x-4">
<div class="w-12 h-12 bg-gray-200 rounded-full flex items-center justify-center">
<svg class="w-6 h-6 text-gray-600" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z"/>
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10z"/>
</svg>
</div>
<div>
<h3 class="text-lg font-medium text-gray-900">{{ userName }}</h3>
<p class="text-sm text-gray-500">{{ userRole }}</p>
</div>
</div>
<!-- Form -->
<form @submit.prevent="handleSubmit" class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
Email
</label>
<input
v-model="email"
type="email"
:class="inputClasses"
placeholder="Enter your email"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
Message
</label>
<textarea
v-model="message"
rows="3"
:class="inputClasses"
placeholder="Enter your message"
></textarea>
</div>
<div class="flex space-x-2">
<button type="submit" :class="buttonClasses.primary">
Send Message
</button>
<button type="button" :class="buttonClasses.secondary" @click="reset">
Reset
</button>
</div>
</form>
<!-- Responsive grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mt-6">
<div v-for="item in items" :key="item.id" :class="cardClasses">
<h4 class="font-semibold text-gray-900">{{ item.title }}</h4>
<p class="text-sm text-gray-600 mt-1">{{ item.description }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TailwindExample',
data() {
return {
title: 'Tailwind CSS with Vue',
subtitle: 'Utility-first CSS framework',
userName: 'John Doe',
userRole: 'Frontend Developer',
email: '',
message: '',
items: [
{ id: 1, title: 'Responsive', description: 'Mobile-first approach' },
{ id: 2, title: 'Utility-First', description: 'Compose any design' },
{ id: 3, title: 'Component-Friendly', description: 'Works great with Vue' }
]
}
},
computed: {
inputClasses() {
return 'w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent'
},
buttonClasses() {
return {
primary: 'btn btn-primary disabled:opacity-50',
secondary: 'btn bg-gray-200 text-gray-800 hover:bg-gray-300'
}
},
cardClasses() {
return 'card p-4 hover:shadow-lg transition-shadow duration-200'
}
},
methods: {
handleSubmit() {
console.log('Form submitted:', { email: this.email, message: this.message })
},
reset() {
this.email = ''
this.message = ''
}
}
}
</script>
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import './assets/css/tailwind.css'
createApp(App).mount('#app')
Install Tailwind CSS with PostCSS configuration and include the CSS file in your main.js. Configure the content paths in tailwind.config.js to include all Vue files for proper purging. Use utility classes directly in templates or create component classes with @layer directive. Tailwind’s responsive prefixes (sm:, md:, lg:) work seamlessly with Vue’s conditional rendering. Combine with Vue’s computed properties for dynamic class generation. The framework provides excellent development experience with IntelliSense support in modern editors.
Best Practice Note:
This is the same approach we use when integrating Tailwind CSS with Vue components in CoreUI enterprise projects. Configure proper purging for production builds, use @layer directive for custom components, leverage Vue’s computed properties for dynamic classes, and consider using Tailwind’s component extraction for repeated patterns.