Vue.js and TailwindCSS make a great team when it comes to creating beautiful scalable apps. Vue’s component-based approach makes it a great fit for use with Tailwind’s intuitive and plentiful utility classes.
The latest version of TailwindCSS (version 4) just released in January 2025. It brings even more powerful styling tools, a streamlined setup process, and more! Let’s see how to use it in a Vue 3 project.
First let’s create a new Vue project from scratch to style with Tailwind.
npm init vue@latest
You can choose whatever options you’d like, it doesn’t matter for this tutorial. If you’re interested, I chose these.
Next we’ll install TailwindCSS with it’s first party Vite plugin.
npm install tailwindcss @tailwindcss/vite
// vite.config.ts|js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import tailwindcss from "@tailwindcss/vite";
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
tailwindcss(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})
src/assets/main.css
/* /src/assets/main.css */
@import "tailwindcss";
<!--App.vue-->
<template>
<h1 class="bg-blue-500 text-white p-5">Hello Tailwind CSS!</h1>
</template>
If the default tailwind theme is enough for you, then there’s no need to do anything else but you might want to configure it for your project’s unique needs. No problem, head on over to tailwind.config.ts
… wait… no… that’s not right. In Tailwind v4 you can configure your theme styles in a .css
file! What a novel idea! Configuring styles in a .css
file instead of a .js
file 🤪
/* /src/assets/main.css */
@import "tailwindcss";
@theme {
/* extend the default theme with custom variables */
--color-primary: oklch(0.53 0.12 118.34);
/* or override the default theme */
--breakpoint-sm: 30rem;
}
These configs are called “theme variables” in the TailwindCSS docs. They are used to generate utility classes but also compile to regular ol’ CSS custom properties (variables) of the same name so you can use them in HTML via inline styles…
<div style="background-color: var(--color-primary)">
<!-- ... -->
</div>
or in CSS files…
p {
background-color: var(--color-primary);
}
or via JavaScript…
window.getComputedStyle(document.body).getPropertyValue('--color-primary')
They aren’t quite the same thing as CSS variables defined in :root
as they’re also used for utility class generation, thus they are defined under a Tailwind specific @theme directive. You can learn more about how these theme variables map to their generated class names in the TailwindCSS docs.
One really big win in Tailwind CSS version 4 is built in support for container queries. Take this simple PostCard
component for example.
<!--PostCard.vue-->
<template>
<article class="@container bg-white rounded-lg shadow-sm">
<!-- Card content adapts based on container width -->
<!-- flex makes image and title side by side at larger sizes
(and stacked at smaller sizes) -->
<div class="@md:flex @md:items-center p-4 gap-4">
<img :src="post.image" class="@md:w-1/3 w-full rounded-lg object-cover" />
<span>{{ post.title }}</span>
</div>
</article>
</template>
<script setup lang="ts">
defineProps<{
post: {
title: string
image: string
}
}>()
</script>
On larger screens it will display the image and title side by side but then on smaller screens they will stack.
BUT it’s not just screen size it accounts for. The containing element’s size is also taken into account. Meaning that if we use the component within another element that has a restricted width, we’ll get the stacked version of the card even if the site is being viewed at full desktop size.
<div class="w-96">
<ProductCard />
</div>
TailwindCSS version 4 also comes with new utility classes for applying CSS transforms in a 3d space. Checkout this cool example of a transformed blog post card from the version launch announcement.
The above could be accomplished in a similar fashion in Vue with code that looks something like this.
<div class="perspective-distant">
<PostCard class="rotate-x-51 rotate-z-43 transform-3d"/>
</div>
In TailwindCSS version 4 there are plenty more options for creating different gradient variants including radial, conic, and linear gradients (with custom angles).
conic, radial, and linear gradients showcased in use on circle elements created with divs and TailwindCSS utility classes
<div class="flex items-center gap-4 mt-10 ml-10">
<div class="size-24 rounded-full bg-conic/[in_hsl_longer_hue] from-red-600 to-red-600"></div>
<div class="size-24 rounded-full bg-radial-[at_25%_25%] from-white to-zinc-900 to-75%"></div>
<div class="size-24 rounded-full bg-linear-45 from-indigo-500 via-purple-500 to-pink-500"></div>
<div class="size-24 rounded-full bg-linear-0 from-indigo-500 via-red-500 to-orange-500"></div>
</div>
The Vue.js Transition
component has an appear
prop that’s useful for animating elements onto the page when it first loads. With TailwindCSS version 4, we can omit JavaScript from the picture altogether with the starting
variant.
<div class="starting:opacity-0 transition-all duration-1000 size-24 rounded-full bg-red-800"></div>
This accomplished with the @starting-style
feature of CSS. Do note that this is NOT supported on all browsers yet but as a progressive enhancement it would work great!
In times past it was common to use JavaScript to resize a textarea to fit it’s content. No more! Now it’s possible with the TailwindCSS field-sizing-content
class alone.
<textarea class="field-sizing-content border-gray-500 border w-full rounded p-3"></textarea>
TailwindCSS version 4 added the a new not-*
variant which adds support for the CSS :not()
pseudo-class. This means we can style elements with less on overrides which is useful for avoiding specificity issues, typing out less classes, and enjoying an easier to follow mental model.
In the below example we are able to easily handle the various states of a RouterLink with a combination of variants combined with not-
.
<RouterLink
to="/"
class="
transition-colors
duration-200
not-hover:text-white
not-active:text-white
hover:text-blue-500
active:text-blue-700
not-hover:bg-blue-500
not-active:bg-blue-500
hover:bg-blue-100
active:bg-blue-200
"
>
Home
</RouterLink>
As an added bonus, it also supports negating media queries and supports rules.
<div class="not-supports-hanging-punctuation:px-4">
<!-- ... -->
</div>
Tailwind version 4 is officially stable and ready to use in your Vue applications and it obviously comes with some really nice enhancements. In this article we’ve reviewed some of the ones I find most interesting but checkout the full release announcement for even more details.
Ready to get started? Just install Tailwind as discussed above or checkout the Nuxt UI v3 (alpha) library, it’s already compatible and it can be installed in a regular Vue or Nuxt project.
Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.
More than 200.000 users have already joined us. You are welcome too!
© All rights reserved. Made with ❤️ by BitterBrains, Inc.