Nuxt is highly flexible when it comes to styling. Write your own styles, or reference local and external stylesheets. You can use CSS preprocessors, CSS frameworks, UI libraries and Nuxt modules to style your application.
If you're writing local stylesheets, the natural place to put them is the assets/ directory.
You can import stylesheets in your pages, layouts and components directly.
You can use a JavaScript import, or a CSS @import statement.
<script>
// Use a static import for server-side compatibility
import '~/assets/css/first.css'
// Caution: Dynamic imports are not server-side compatible
import('~/assets/css/first.css')
</script>
<style>
@import url("~/assets/css/second.css");
</style>
You can also use the css property in the Nuxt configuration.
The natural place for your stylesheets is the assets/ directory. You can then reference its path and Nuxt will include it to all the pages of your application.
export default defineNuxtConfig({
  css: ['~/assets/css/main.css'],
})
Place your local fonts files in your public/ directory, for example in public/fonts. You can then reference them in your stylesheets using url().
@font-face {
  font-family: 'FarAwayGalaxy';
  src: url('/fonts/FarAwayGalaxy.woff') format('woff');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}
Then reference your fonts by name in your stylesheets, pages or components:
<style>
h1 {
  font-family: 'FarAwayGalaxy', sans-serif;
}
</style>
You can also reference stylesheets that are distributed through npm. Let's use the popular animate.css library as an example.
npm install animate.css
yarn add animate.css
pnpm install animate.css
bun install animate.css
Then you can reference it directly in your pages, layouts and components:
<script>
import 'animate.css'
</script>
<style>
@import url("animate.css");
</style>
The package can also be referenced as a string in the css property of your Nuxt configuration.
export default defineNuxtConfig({
  css: ['animate.css'],
})
You can include external stylesheets in your application by adding a link element in the head section of your nuxt.config file. You can achieve this result using different methods. Note that local stylesheets can also be included this way.
You can manipulate the head with the app.head property of your Nuxt configuration:
export default defineNuxtConfig({
  app: {
    head: {
      link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }],
    },
  },
})
You can use the useHead composable to dynamically set a value in your head in your code.
useHead({
  link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }],
})
Nuxt uses unhead under the hood, and you can refer to its full documentation.
If you need more advanced control, you can intercept the rendered html with a hook and modify the head programmatically.
Create a plugin in ~/server/plugins/my-plugin.ts like this:
export default defineNitroPlugin((nitro) => {
  nitro.hooks.hook('render:html', (html) => {
    html.head.push('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">')
  })
})
External stylesheets are render-blocking resources: they must be loaded and processed before the browser renders the page. Web pages that contain unnecessarily large styles take longer to render. You can read more about it on web.dev.
To use a preprocessor like SCSS, Sass, Less or Stylus, install it first.
npm install -D sass
npm install -D less
npm install -D stylus
The natural place to write your stylesheets is the assets directory.
You can then import your source files in your app.vue (or layouts files) using your preprocessor's syntax.
<style lang="scss">
@use "~/assets/scss/main.scss";
</style>
Alternatively, you can use the css property of your Nuxt configuration.
export default defineNuxtConfig({
  css: ['~/assets/scss/main.scss'],
})
If you need to inject code in pre-processed files, like a Sass partial with color variables, you can do so with the Vite preprocessors options.
Create some partials in your assets directory:
$primary: #49240F;
$secondary: #E4A79D;
$primary: #49240F
$secondary: #E4A79D
Then in your nuxt.config :
export default defineNuxtConfig({
  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: '@use "~/assets/_colors.scss" as *;',
        },
      },
    },
  },
})
export default defineNuxtConfig({
  vite: {
    css: {
      preprocessorOptions: {
        sass: {
          additionalData: '@use "~/assets/_colors.sass" as *\n',
        },
      },
    },
  },
})
Nuxt uses Vite by default. If you wish to use webpack instead, refer to each preprocessor loader documentation.
Vite has made available an experimental option which can speed up using preprocessors.
You can enable this in your nuxt.config:
export default defineNuxtConfig({
  vite: {
    css: {
      preprocessorMaxWorkers: true, // number of CPUs minus 1
    },
  },
})
One of the best things about Vue and SFC is how great it is at naturally dealing with styling. You can directly write CSS or preprocessor code in the style block of your components file, therefore you will have fantastic developer experience without having to use something like CSS-in-JS. However if you wish to use CSS-in-JS, you can find 3rd party libraries and modules that support it, such as pinceau.
You can refer to the Vue docs for a comprehensive reference about styling components in SFC.
You can leverage Vue SFC features to style your components with class and style attributes.
<script setup lang="ts">
const isActive = ref(true)
const hasError = ref(false)
const classObject = reactive({
  'active': true,
  'text-danger': false,
})
</script>
<template>
  <div
    class="static"
    :class="{ 'active': isActive, 'text-danger': hasError }"
  />
  <div :class="classObject" />
</template>
<script setup lang="ts">
const isActive = ref(true)
const error = ref(null)
const classObject = computed(() => ({
  'active': isActive.value && !error.value,
  'text-danger': error.value && error.value.type === 'fatal',
}))
</script>
<template>
  <div :class="classObject" />
</template>
<script setup lang="ts">
const isActive = ref(true)
const errorClass = ref('text-danger')
</script>
<template>
  <div :class="[{ active: isActive }, errorClass]" />
</template>
<script setup lang="ts">
const activeColor = ref('red')
const fontSize = ref(30)
const styleObject = reactive({ color: 'red', fontSize: '13px' })
</script>
<template>
  <div :style="{ color: activeColor, fontSize: fontSize + 'px' }" />
  <div :style="[baseStyles, overridingStyles]" />
  <div :style="styleObject" />
</template>
Refer to the Vue docs for more information.
v-bindYou can reference JavaScript variable and expression within your style blocks with the v-bind function. The binding will be dynamic, meaning that if the variable value changes, the style will be updated.
<script setup lang="ts">
const color = ref('red')
</script>
<template>
  <div class="text">
    hello
  </div>
</template>
<style>
.text {
  color: v-bind(color);
}
</style>
The scoped attribute allows you to style components in isolation. The styles declared with this attribute will only apply to this component.
<template>
  <div class="example">
    hi
  </div>
</template>
<style scoped>
.example {
  color: red;
}
</style>
You can use CSS Modules with the module attribute. Access it with the injected $style variable.
<template>
  <p :class="$style.red">
    This should be red
  </p>
</template>
<style module>
.red {
  color: red;
}
</style>
SFC style blocks support preprocessor syntax. Vite comes with built-in support for .scss, .sass, .less, .styl and .stylus files without configuration. You just need to install them first, and they will be available directly in SFC with the lang attribute.
<style lang="scss">
  /* Write scss here */
</style>
<style lang="sass">
  /* Write sass here */
</style>
<style lang="less">
  /* Write less here */
</style>
<style lang="stylus">
  /* Write stylus here */
</style>
You can refer to the Vite CSS docs and the @vitejs/plugin-vue docs. For webpack users, refer to the vue loader docs.
Nuxt comes with postcss built-in. You can configure it in your nuxt.config file.
export default defineNuxtConfig({
  postcss: {
    plugins: {
      'postcss-nested': {},
      'postcss-custom-media': {},
    },
  },
})
For proper syntax highlighting in SFC, you can use the postcss lang attribute.
<style lang="postcss">
  /* Write postcss here */
</style>
By default, Nuxt comes with the following plugins already pre-configured:
@import ruleurl() statementsIf you need to style different parts of your application completely differently, you can use layouts. Use different styles for different layouts.
<template>
  <div class="default-layout">
    <h1>Default Layout</h1>
    <slot />
  </div>
</template>
<style>
.default-layout {
  color: red;
}
</style>
Nuxt isn't opinionated when it comes to styling and provides you with a wide variety of options. You can use any styling tool that you want, such as popular libraries like UnoCSS or Tailwind CSS.
The community and the Nuxt team have developed plenty of Nuxt modules to make the integration easier. You can discover them on the modules section of the website. Here are a few modules to help you get started:
Nuxt modules provide you with a good developer experience out of the box, but remember that if your favorite tool doesn't have a module, it doesn't mean that you can't use it with Nuxt! You can configure it yourself for your own project. Depending on the tool, you might need to use a Nuxt plugin and/or make your own module. Share them with the community if you do!
You can use the Nuxt Google Fonts module to load Google Fonts.
If you are using UnoCSS, note that it comes with a web fonts presets to conveniently load fonts from common providers, including Google Fonts and more.
Nuxt comes with the same <Transition> element that Vue has, and also has support for the experimental View Transitions API.
We would recommend using Fontaine to reduce your CLS. If you need something more advanced, consider creating a Nuxt module to extend the build process or the Nuxt runtime.
You can do the following to speed-up the download of your global CSS files:
Most of these things should be done for you automatically if you're using modern platforms like Cloudflare, Netlify or Vercel. You can find an LCP optimization guide on web.dev.
If all of your CSS is inlined by Nuxt, you can (experimentally) completely stop external CSS files from being referenced in your rendered HTML. You can achieve that with a hook, that you can place in a module, or in your Nuxt configuration file.
export default defineNuxtConfig({
  hooks: {
    'build:manifest': (manifest) => {
      // find the app entry, css list
      const css = Object.values(manifest).find(options => options.isEntry)?.css
      if (css) {
        // start from the end of the array and go to the beginning
        for (let i = css.length - 1; i >= 0; i--) {
          // if it starts with 'entry', remove it from the list
          if (css[i].startsWith('entry')) {
            css.splice(i, 1)
          }
        }
      }
    },
  },
})