Skip to content

Fix: UnoCSS Not Working — Classes Not Generating, Presets Missing, or Attributify Mode Broken

FixDevs ·

Quick Answer

How to fix UnoCSS issues — Vite plugin setup, preset configuration, attributify mode, icons preset, shortcuts, custom rules, and integration with Next.js, Nuxt, and Astro.

The Problem

UnoCSS utility classes render in the HTML but have no effect:

<div class="p-4 bg-blue-500 text-white rounded-lg">
  No styles applied
</div>

Or the @unocss/preset-icons icons don’t show:

<div class="i-mdi-home w-6 h-6" />
<!-- Empty div — no icon -->

Or attributify mode types don’t work:

<div bg="blue-500" p="4" text="white">
  TypeScript error: Property 'bg' does not exist on type...
</div>

Or generated CSS is empty even though classes are used:

[UnoCSS] No CSS generated. Make sure the content is scanned correctly.

Why This Happens

UnoCSS is an on-demand atomic CSS engine. It scans your source files for class names and generates only the CSS for classes you actually use:

  • UnoCSS generates nothing by default — without presets, UnoCSS has zero built-in utility classes. The @unocss/preset-wind or @unocss/preset-uno preset adds Tailwind-compatible utilities. Without a preset, every class name is unrecognized and produces no CSS.
  • The scanner must find your files — UnoCSS reads files listed in its content configuration or detected by the Vite plugin. If your components are in directories the scanner doesn’t check, no classes are extracted and no CSS is generated.
  • Icon classes need icon data installed@unocss/preset-icons generates SVG icons as CSS masks. The actual SVG data comes from @iconify-json/* packages. Using i-mdi-home without installing @iconify-json/mdi produces an empty class.
  • Attributify mode requires explicit opt-in — the @unocss/preset-attributify preset enables HTML attribute syntax (bg="blue-500"), but TypeScript and JSX need type augmentation to accept these non-standard attributes.

Fix 1: Set Up UnoCSS with Vite

npm install -D unocss
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import UnoCSS from 'unocss/vite';

export default defineConfig({
  plugins: [
    UnoCSS(),  // Must be before framework plugin
    react(),
  ],
});
// uno.config.ts — configuration file
import {
  defineConfig,
  presetUno,           // Default preset (Tailwind + Windi CSS compatible)
  presetAttributify,   // Optional: attribute mode
  presetIcons,         // Optional: icon support
  presetTypography,    // Optional: prose styles
  presetWebFonts,      // Optional: web font loading
  transformerDirectives,    // Optional: @apply in CSS
  transformerVariantGroup,  // Optional: hover:(bg-blue text-white)
} from 'unocss';

export default defineConfig({
  presets: [
    presetUno(),           // Core utilities (Tailwind-like)
    presetAttributify(),   // <div text="sm white" bg="blue-500">
    presetIcons({
      scale: 1.2,
      cdn: 'https://esm.sh/',  // Fallback CDN if icon package isn't installed
      extraProperties: {
        'display': 'inline-block',
        'vertical-align': 'middle',
      },
    }),
    presetTypography(),
    presetWebFonts({
      fonts: {
        sans: 'Inter:400,500,600,700',
        mono: 'Fira Code:400,500',
      },
    }),
  ],
  transformers: [
    transformerDirectives(),    // Enable @apply in CSS files
    transformerVariantGroup(),  // Enable variant groups: hover:(bg-blue text-white)
  ],
});
// src/main.tsx — import the virtual CSS file
import 'virtual:uno.css';

// Optional: import reset
import '@unocss/reset/tailwind.css';

Fix 2: Use Utility Classes

With presetUno(), you get Tailwind-compatible utilities:

// All standard Tailwind classes work
function Card() {
  return (
    <div className="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
      <div className="md:flex">
        <div className="p-8">
          <h2 className="text-2xl font-bold text-gray-900 mb-2">Card Title</h2>
          <p className="text-gray-600 leading-relaxed">
            Card content with UnoCSS utility classes.
          </p>
          <button className="mt-4 px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
            Action
          </button>
        </div>
      </div>
    </div>
  );
}

// Variant groups — group hover, focus, etc.
// Requires transformerVariantGroup
<button className="hover:(bg-blue-600 text-white scale-105) transition-all">
  Hover me
</button>

// Arbitrary values — same as Tailwind
<div className="w-[300px] h-[200px] bg-[#1a1a2e] text-[14px]">
  Custom values
</div>

// Dark mode
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
  Adapts to dark mode
</div>

Fix 3: Icons with preset-icons

# Install icon collections you need
npm install -D @iconify-json/mdi         # Material Design Icons (7000+ icons)
npm install -D @iconify-json/lucide      # Lucide icons
npm install -D @iconify-json/heroicons   # Heroicons
npm install -D @iconify-json/logos       # Technology logos
npm install -D @iconify-json/ph          # Phosphor icons

# Or install ALL iconify icons (large)
# npm install -D @iconify/json
// uno.config.ts
import { defineConfig, presetUno, presetIcons } from 'unocss';

export default defineConfig({
  presets: [
    presetUno(),
    presetIcons({
      scale: 1.2,
      extraProperties: {
        'display': 'inline-block',
        'vertical-align': 'middle',
      },
      // Custom icon collections
      collections: {
        custom: {
          // Inline SVG icons
          'circle': '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" fill="currentColor"/></svg>',
        },
      },
    }),
  ],
});
// Usage — class name format: i-{collection}-{name}
function IconExamples() {
  return (
    <div className="flex gap-4 items-center">
      {/* Material Design Icons */}
      <span className="i-mdi-home text-2xl text-blue-500" />
      <span className="i-mdi-account text-2xl text-green-500" />
      <span className="i-mdi-settings text-2xl" />

      {/* Lucide icons */}
      <span className="i-lucide-search text-xl" />
      <span className="i-lucide-menu text-xl" />

      {/* Heroicons */}
      <span className="i-heroicons-bell-solid text-xl text-red-500" />

      {/* Technology logos */}
      <span className="i-logos-react text-3xl" />
      <span className="i-logos-vue text-3xl" />

      {/* Custom icon */}
      <span className="i-custom-circle text-xl text-purple-500" />

      {/* Icon button */}
      <button className="p-2 rounded-lg hover:bg-gray-100 transition-colors">
        <span className="i-mdi-delete text-xl text-red-500" />
      </button>
    </div>
  );
}

Fix 4: Shortcuts and Custom Rules

// uno.config.ts
import { defineConfig, presetUno } from 'unocss';

export default defineConfig({
  presets: [presetUno()],

  // Shortcuts — alias multiple classes to one name
  shortcuts: {
    // Static shortcut
    'btn': 'px-4 py-2 rounded-lg font-semibold inline-flex items-center justify-center transition-colors cursor-pointer',
    'btn-primary': 'btn bg-blue-500 text-white hover:bg-blue-600',
    'btn-secondary': 'btn bg-gray-200 text-gray-800 hover:bg-gray-300',
    'btn-danger': 'btn bg-red-500 text-white hover:bg-red-600',

    // Card shortcuts
    'card': 'bg-white rounded-xl shadow-md p-6 dark:bg-gray-800',
    'card-hover': 'card hover:shadow-lg transition-shadow',

    // Layout shortcuts
    'flex-center': 'flex items-center justify-center',
    'flex-between': 'flex items-center justify-between',

    // Dynamic shortcuts (regex)
    [/^badge-(.*)$/, ([, c]) => `px-2 py-0.5 rounded-full text-xs font-bold bg-${c}-100 text-${c}-800`],
  },

  // Custom rules — define your own utilities
  rules: [
    // Static rule
    ['custom-scrollbar', {
      'scrollbar-width': 'thin',
      'scrollbar-color': '#888 transparent',
    }],

    // Dynamic rule with regex
    [/^text-shadow-(.+)$/, ([, value]) => ({
      'text-shadow': value === 'sm' ? '0 1px 2px rgba(0,0,0,0.1)'
        : value === 'md' ? '0 2px 4px rgba(0,0,0,0.15)'
        : value === 'lg' ? '0 4px 8px rgba(0,0,0,0.2)'
        : undefined,
    })],

    // Grid auto-fill
    [/^grid-auto-fill-(\d+)$/, ([, minWidth]) => ({
      'display': 'grid',
      'grid-template-columns': `repeat(auto-fill, minmax(${minWidth}px, 1fr))`,
    })],
  ],

  // Theme overrides
  theme: {
    colors: {
      brand: {
        50: '#eff6ff',
        100: '#dbeafe',
        500: '#3b82f6',
        600: '#2563eb',
        900: '#1e3a5f',
      },
    },
    breakpoints: {
      sm: '640px',
      md: '768px',
      lg: '1024px',
      xl: '1280px',
    },
  },
});
// Usage
<button className="btn-primary">Submit</button>
<span className="badge-green">Active</span>
<div className="grid-auto-fill-250 gap-4">
  <div className="card-hover">Card 1</div>
  <div className="card-hover">Card 2</div>
</div>

Fix 5: Attributify Mode

Write utilities as HTML attributes instead of class strings:

// uno.config.ts
import { defineConfig, presetUno, presetAttributify } from 'unocss';

export default defineConfig({
  presets: [
    presetUno(),
    presetAttributify({
      prefix: 'un-',     // Optional: prefix to avoid conflicts (un-bg="blue-500")
      prefixedOnly: false, // Set true to require prefix on all attributes
    }),
  ],
});
// Attributify syntax — group by property
<div
  bg="blue-500 hover:blue-600 dark:blue-800"
  text="white sm lg:xl"
  p="4 md:6"
  m="2 auto"
  border="~ rounded-lg gray-200"
  flex="~ col md:row"
  gap="4"
>
  Styled with attributes
</div>

// Equivalent class-based syntax:
<div className="bg-blue-500 hover:bg-blue-600 dark:bg-blue-800 text-white text-sm lg:text-xl p-4 md:p-6 m-2 mx-auto border border-rounded-lg border-gray-200 flex flex-col md:flex-row gap-4">

TypeScript support for attributify:

// src/attributes.d.ts — or wherever your type declarations go
import type { AttributifyAttributes } from '@unocss/preset-attributify';

declare module 'react' {
  interface HTMLAttributes<T> extends AttributifyAttributes {}
}
// For Vue: declare module '@vue/runtime-dom' { ... }

Fix 6: Framework Integration

Next.js:

npm install -D @unocss/webpack
// next.config.mjs
import UnoCSS from '@unocss/webpack';

export default {
  webpack: (config) => {
    config.plugins.push(UnoCSS());
    return config;
  },
};
// app/layout.tsx
import '@unocss/reset/tailwind.css';
import 'uno.css';

Nuxt:

npm install -D @unocss/nuxt
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@unocss/nuxt'],
});

Astro:

npm install -D unocss
// astro.config.mjs
import { defineConfig } from 'astro/config';
import UnoCSS from 'unocss/astro';

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

Still Not Working?

“No CSS generated” warning — UnoCSS scans files based on the plugin’s content configuration. For Vite, it auto-detects files served by Vite. For webpack/Next.js, you may need to specify content.filesystem in uno.config.ts to point to your source files. Also verify that virtual:uno.css (Vite) or uno.css (webpack) is imported in your entry file.

Classes work in HTML but not in JSX — React uses className, not class. UnoCSS scans for both, but make sure your JSX uses className. Also check that string interpolation in template literals is handled: className={`text-${color}-500`} won’t be detected because UnoCSS can’t evaluate runtime expressions. Use safelist for dynamic classes.

Icons are invisible (element has correct class but no visual) — the icon collection package isn’t installed. i-mdi-home requires @iconify-json/mdi. Check the collection name matches the package: i-lucide-* needs @iconify-json/lucide, i-heroicons-* needs @iconify-json/heroicons. After installing, restart the dev server.

Attributify classes conflict with existing HTML attributes — if text="white" conflicts with a component’s text prop, use the prefix option: presetAttributify({ prefix: 'un-' }). Then use un-text="white" instead.

For related CSS utility issues, see Fix: Tailwind CSS Not Working and Fix: Panda CSS Not Working.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles