We already know how to get a Nuxt application up and running and how to use the various building blocks to assemble it. Now it's time to give our pages a reasonably decent look. In theory, we could handle that ourselves - using CSS styles defined inside the <style> sections of individual components. If you're confident with CSS, you can have a nice-looking website pretty quickly.
Most of the time, though, we don't want to rebuild everything from scratch. In this article you'll first see how easy it is to import ready-made CSS files, and then we'll go through two example integrations with projects that make styling much easier: Tailwind CSS and Open Props.
Import CSS
If you're migrating an older project, you might already have your .css style files ready. Or you may be using a service that ships its styles as a standalone .css file (i.e. when using Bootstrap from a CDN). Nuxt offers a very simple integration for these cases.
Upload your style files into the /app/assets folder (usually into /css sub-directory for better clarity). Then reference them via the css option in your nuxt.config.ts config file, i.e.:
export default defineNuxtConfig({
css: ['~/assets/css/main.css']
// ...
})
The imported styles are then automatically available across the whole application.
If needed, you can also load them individually per component - either in the <script setup> section:
<script setup lang="ts">
import '~/assets/css/main.css'
</script>
or inside <style>:
<style>
@import url("~/assets/css/main.css");
</style>
Even with just this, you can achieve quite a lot. For examples of integrations with more advanced CSS libraries, keep reading.
Tailwind CSS
Tailwind CSS is a CSS framework for styling websites and applications. Unlike the traditional CSS approach, you don't write your own classes; instead, you compose the final look from a large set of predefined utility classes, which enables fast and flexible styling and layouts. Tailwind itself is also highly adaptable and configurable - for example when it comes to defining color schemes.
It deserves an article of its own (which I originally planned, and maybe I'll get to it eventually). For me it's currently the number one choice for building a website's look. It requires a slightly different philosophy, but I adjusted in a day, and I don't want to do it any other way now. I migrated couple of my personal projects to Tailwind CSS already, where the original design was done “by hand”. Converting them into the corresponding Tailwind structure usually took just one evening.
It's fair to note it's not a CSS framework “for purists” because it does impose some constraints. Others criticize it for creating too much abstraction and giving users the illusion that they understand what they're doing - until they hit a trap they can't resolve because they don't really know the underlying CSS. My experience, however, is that it will be enough in vast majority of situations, and its efficiency gives you more time to handle the remaining edge cases.
Nuxt integration
Getting started with Tailwind CSS is now very straightforward. The creators ship a plugin for Vite (the build tool Nuxt uses), which you just install together with the main library:
npm install tailwindcss @tailwindcss/vite
Then add the plugin to nuxt.config.ts along with a reference to the CSS file that imports Tailwind:
import tailwindcss from "@tailwindcss/vite";
export default defineNuxtConfig({
// Vite integration plugin
vite: {
plugins: [
tailwindcss(),
],
},
// CSS file importing Tailwind classes
css: ['~/assets/css/tailwind.css']
// other config...
});
The final step is creating a simple CSS file that you load via the css option. Its content can be as minimal as:
@import "tailwindcss";
Demo app
You can try the approach above in practice. The source code for the demo implementation is here: nuxt-tailwind @ GitHub
The demo focuses on both integration and usage. All the visual behavior is in /app/app.vue. It contains several examples of how Tailwind CSS can be applied. It also shows that utility classes can be freely combined with classic “plain” CSS. In /app/assets/css/tailwind.css you can see how easily you can extend Tailwind CSS' default @theme with your own colors or fonts if the base palette isn't enough.
Based on a personal bad experience, I also decided to add one cautionary example to the demo alongside the positive ones. I'm risking that it might inspire someone to use it and fall into the same trap I once did, but hopefully it will save many of you from unnecessary problems. What is it about? The Tailwind CSS authors encourage you to use utility classes directly in HTML templates as attributes on individual elements whenever possible. Not everyone (hi, former me) likes that, because:
- Class lists can get really long
- The same definitions repeat
At the same time, a beginner - armed with general programming truths and ready to “fix” things - will sooner or later discover the @apply directive, which seems to allow defining reusable classes in CSS files. And to be fair: it does allow it, and it works perfectly. The problem is that it severely obfuscates the implementation. After a few months, you forget you styled a <div> globally and wonder why your inline definitions don't apply. The demo app includes a clear example with the color of an HTML link at the bottom of the page. I wrote more about this in this article.
End of the diversion. Definitely take a look at Tailwind CSS - you might end up liking it like I do, along with thousands of other developers who prioritize speed and efficiency over perfect control via native CSS.
P.S.: Previously I handled the integration via a dedicated Nuxt module, which was a bit easier to set up (install + add the module). I'm not sure about its current status, though. After Tailwind v4 was released, it stopped working for me. Its new version has been stuck in beta for months, and it almost looks like its historical role is over. If the situation changes, I'll come back and update this article.
Open Props
Open Props is based on a similar idea to Tailwind CSS - it provides a set of prebuilt styles that may constrain you a bit, but in return you don't have to reinvent the wheel everywhere. Instead of shipping complete prebuilt CSS classes, Open Props encodes the styling information as CSS variables, which you can use and compose inside your own classes. To be honest: Tailwind v4 also uses CSS variables, and you may have already tasted them (even unknowingly) - see how the demo app extends the default @theme.
Open Props sits one level lower. It doesn't abstract away writing CSS entirely; it “only” provides ready-made, proven values. That gives you more freedom, but it's a bit more work. The main advantage is how lightweight and scalable it is - the library lets you use only the small subset you actually need.
Nuxt integration
Because Nuxt (Vue) can't replace CSS variables with actual values at build time by itself, you need to add a CSS processor (postcss in my demo) along with a plugin that performs the substitution after being fed the definitions from OpenProps. That's about the only catch; otherwise you just import the open-props NPM package and start building CSS definitions. There is a showcase of a few styles in the demo.
What I like most about Open Props are the ready-made gradients - smooth transitions between colors. Sure, it's not that hard to write them yourself in plain CSS (I even watched a demo of building a “gradient editor” recently), but if you're not a web designer at heart, who wants to grind through that?
The source code for the demo implementation is here:
nuxt-openprops @ GitHub
Summary
We've shown how to load internal and external CSS files into a Nuxt app, plus two examples of CSS libraries and how to integrate them. There are, of course, many other options. If you have a favorite UI/styling library, check whether it already has a Nuxt integration (most commonly in the form of a module). If not, it may be enough just to load the resulting CSS files the way we described at the start and use them directly. And if you get stuck, reach out and we can try to figure something out together.
In the upcoming tutorial article, we'll move from general styling to more advanced UI libraries, which provide not only styles but also prebuilt UI components. We'll look at a few of them along with examples of integrating them into Nuxt.
Top comments (0)