/ astro-integrations / How to Integrate UnoCSS with Astro: Complete Guide
astro-integrations 9 min read

How to Integrate UnoCSS with Astro: Complete Guide

Step-by-step guide to integrating UnoCSS with your Astro website.

How to Integrate UnoCSS with Astro: Complete Guide

UnoCSS is an atomic CSS engine that is instant and on-demand. It generates only the CSS you use, with zero parsing of your stylesheets. If you have used Tailwind CSS, UnoCSS will feel familiar, but it is fundamentally different under the hood. Instead of scanning your code for predefined utility classes, UnoCSS uses a rules engine that can match any pattern you define.

The result is faster builds, smaller CSS output, and extreme flexibility. You can use Tailwind-compatible syntax, Windi CSS syntax, or define completely custom utility patterns. For Astro sites, UnoCSS integrates directly into the build process through a Vite plugin.

Prerequisites

  • Node.js 22.12.0 or higher (Astro 6 dropped Node 18 and Node 20 entirely, and the odd-numbered 19, 21, and 23 releases were never LTS)
  • An Astro project (npm create astro@latest)

Versions used in this guide, fetched from the npm registry and verified on 2026-05-29: astro 6.4.2, unocss 66.7.0, @unocss/astro 66.7.0, @unocss/reset 66.7.0. The unocss umbrella package re-exports every official preset and transformer, so you usually do not install the preset packages individually.

Installation

Install UnoCSS, the Astro integration, and the reset package:

npm install -D unocss@66.7.0 @unocss/astro@66.7.0 @unocss/reset@66.7.0

Configuration

Add UnoCSS to your astro.config.mjs. The official integration documents the import path as unocss/astro, and the integration does not inject a browser style reset by default, so opt in with injectReset: true:

import { defineConfig } from "astro/config";
import UnoCSS from "unocss/astro";

export default defineConfig({
  integrations: [UnoCSS({ injectReset: true })],
});

Astro 6 renders to static HTML by default, so a CSS engine like UnoCSS that runs through a Vite plugin works with no adapter and no output setting. The output: 'hybrid' option was removed in Astro 5, and static and hybrid were merged into the default static mode. You only add an adapter when you opt a route into on-demand rendering with export const prerender = false. UnoCSS itself does not require any of that.

Create a uno.config.ts in your project root. UnoCSS renamed its default preset: presetUno (and the older presetWind) were deprecated in 66.0.0 and renamed to presetWind3, the Tailwind and Windi CSS compatible preset. Use presetWind3 instead of presetUno:

// uno.config.ts
import {
  defineConfig,
  presetWind3,
  presetAttributify,
  presetIcons,
  presetTypography,
  transformerDirectives,
  transformerVariantGroup,
} from "unocss";

export default defineConfig({
  presets: [
    presetWind3(), // Tailwind/Windi CSS compatible utilities (successor to presetUno)
    presetAttributify(), // Attributify mode (optional)
    presetIcons({ scale: 1.2 }), // Icon support (optional)
    presetTypography(), // Prose styles (optional)
  ],
  transformers: [
    transformerDirectives(), // @apply support
    transformerVariantGroup(), // hover:(bg-red text-white)
  ],
  theme: {
    colors: {
      primary: {
        50: "#f0f9ff",
        100: "#e0f2fe",
        500: "#0ea5e9",
        600: "#0284c7",
        700: "#0369a1",
        900: "#0c4a6e",
      },
    },
    fontFamily: {
      sans: ["Inter", "sans-serif"],
      mono: ["JetBrains Mono", "monospace"],
    },
  },
  shortcuts: {
    btn: "px-4 py-2 rounded-lg font-medium transition-colors",
    "btn-primary": "btn bg-primary-500 text-white hover:bg-primary-600",
    "btn-outline":
      "btn border border-gray-300 hover:border-primary-500 hover:text-primary-500",
    container: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8",
  },
});

Import UnoCSS in your main layout:

---
// src/layouts/BaseLayout.astro
import "uno.css";
---

<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  </head>
  <body>
    <slot />
  </body>
</html>

Basic Usage

Use utility classes just like Tailwind CSS:

---
// src/components/Card.astro
export interface Props {
  title: string;
  description: string;
}

const { title, description } = Astro.props;
---

<div class="bg-white rounded-xl shadow-md p-6 hover:shadow-lg transition-shadow">
  <h3 class="text-xl font-bold text-gray-900 mb-2">{title}</h3>
  <p class="text-gray-600 leading-relaxed">{description}</p>
  <a href="#" class="btn-primary inline-block mt-4">Learn More</a>
</div>

Attributify Mode

The attributify preset lets you split classes into HTML attributes for cleaner markup:

<div
  bg="white"
  rounded="xl"
  shadow="md hover:lg"
  p="6"
  transition="shadow"
>
  <h3 text="xl gray-900" font="bold" mb="2">Title</h3>
  <p text="gray-600" leading="relaxed">Description</p>
</div>

This produces the same CSS but keeps your HTML more readable when you have many utilities.

Variant Groups

The variant group transformer lets you group utilities under a shared variant:

<!-- Without variant groups -->
<div class="hover:bg-blue-500 hover:text-white hover:shadow-lg hover:scale-105">
  Hover me
</div>

<!-- With variant groups -->
<div class="hover:(bg-blue-500 text-white shadow-lg scale-105)">
  Hover me
</div>

Icons with UnoCSS

The icons preset lets you use any icon from Iconify directly as CSS classes. Install the Iconify JSON collections for the icon sets you want (versions verified on 2026-05-29: @iconify-json/heroicons 1.2.3, @iconify-json/lucide 1.2.111):

npm install -D @iconify-json/heroicons @iconify-json/lucide
<!-- Use icons as classes -->
<span class="i-heroicons-home w-6 h-6"></span>
<span class="i-lucide-search w-5 h-5 text-gray-400"></span>
<span class="i-heroicons-moon w-5 h-5 dark:i-heroicons-sun"></span>

No icon component libraries, no SVG imports. UnoCSS generates the icon as a CSS mask image.

Custom Rules

Define your own utility patterns:

// uno.config.ts
export default defineConfig({
  rules: [
    // Custom utility: text-balance
    ["text-balance", { "text-wrap": "balance" }],
    // Custom utility: content-grid
    [
      "content-grid",
      {
        display: "grid",
        "grid-template-columns":
          "[full-start] 1fr [content-start] min(65ch, 100%) [content-end] 1fr [full-end]",
      },
    ],
  ],
});

Use them like any other utility:

<div class="rounded-xl p-6">
  <p class="text-balance">This text wraps more evenly across lines.</p>
</div>

Dark Mode

UnoCSS supports dark mode with the same dark: prefix:

<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
  <h1 class="text-primary-600 dark:text-primary-400">
    Works in both modes
  </h1>
</div>

Production Tips

  1. Use shortcuts for repeated patterns. Instead of writing px-4 py-2 rounded-lg font-medium on every button, define a btn shortcut in your config. This keeps your HTML clean and makes design changes easier.

  2. Check the generated CSS. Run npx unocss to see what CSS UnoCSS generates. This helps you catch unused rules and verify that your custom patterns produce the correct output.

  3. Combine presets strategically. Start with presetUno() for Tailwind compatibility. Add presetIcons() only if you use CSS icons. Add presetTypography() only if you need prose styles. Each preset adds to the processing time.

  4. Use safelist for dynamic classes. If you generate class names dynamically, add them to the safelist in your config so UnoCSS includes them in the output.

  • Migrate from Tailwind gradually. UnoCSS is compatible with most Tailwind utilities. You can switch your build from Tailwind to UnoCSS and most of your existing classes will work without changes.

  • Know the Wind3 to Wind4 path. presetWind3 is the direct, stable successor to the old presetUno and is what the official Astro example uses. presetWind4 is the newer Tailwind 4 compatible preset and is feature compatible with Wind3 with some adjusted theme keys. Start on presetWind3 for the smoothest migration off presetUno, then evaluate presetWind4 once your icon and typography presets confirm compatibility.

  • Alternatives to Consider

    • Tailwind CSS if you prefer a larger ecosystem with more community plugins and official component libraries.
    • Windi CSS (now deprecated) was the predecessor. Its ideas live on in UnoCSS.
    • Open Props if you prefer CSS custom properties over utility classes.

    Common Errors and Fixes

    • No styles at all after install. UnoCSS only generates CSS for the virtual uno.css module, so it does nothing until you import it. Add import "uno.css"; once in your root layout. This is separate from the reset.

    • presetUno is not exported or a deprecation warning on install. @unocss/preset-uno and @unocss/preset-wind were deprecated and renamed to @unocss/preset-wind3 in 66.0.0. Replace presetUno() with presetWind3() and import it from unocss (or from @unocss/preset-wind3). The old import keeps working through the alias for now but logs a deprecation notice.

    • Default browser margins and inconsistent box sizing. The Astro integration does not inject a style reset by default. Either pass injectReset: true (or a path to a reset file) to UnoCSS() in astro.config.mjs, or import a reset manually such as import "@unocss/reset/tailwind.css"; in your layout.

    • @apply does nothing in a <style> block. Directives like @apply are handled by transformerDirectives(), which is not enabled by default. Add it to the transformers array in uno.config.ts.

    • Variant group syntax such as hover:(...) is output literally. That grouping is provided by transformerVariantGroup(). Add it to transformers or write the utilities out the long way.

    • A dynamically constructed class name never renders. UnoCSS is on demand and only emits CSS for class strings it can statically see in your source. Class names assembled at runtime are invisible to the scanner, so list them in the safelist array in your config.

    • An adapter or output setting feels required. It is not for UnoCSS. Astro 6 is static by default, output: 'hybrid' was removed back in Astro 5, and you only add an adapter when a route opts into on-demand rendering with prerender = false. A pure UnoCSS styling setup needs none of that.

    Official Docs and Examples

    Wrapping Up

    UnoCSS brings the utility-first approach to a new level of performance and flexibility. For Astro sites, the integration is seamless through the Vite plugin, build times are noticeably faster than Tailwind, and the generated CSS is as small as it gets. The custom rules engine means you are never limited to predefined utilities. If you want atomic CSS with room to grow, UnoCSS is the engine to build on.

    Sources

    Checked on 2026-05-29.