Integration for Svelte

Our Svelte integration uses the Svelte Preprocessor API, therefore runs before compilation step. This brings some limitations for dynamic changed classes.

If you are using SvelteKit, you can also check out the Vite SvelteKit guide.

Setup Guides

Here are two guides for Svelte and SvelteKit using their starter template.


Install the Svelte WindiCSS Preprocessor plugin from NPM

npm i -D svelte-windicss-preprocess

remove not needed global CSS files to prevent style breaks

- ./public/global.css

remove stylesheet link in index.html

  <!DOCTYPE html>
  <html lang="en">

    <meta charset='utf-8'>
    <meta name='viewport' content='width=device-width,initial-scale=1'>

    <title>Svelte app</title>

    <link rel='icon' type='image/png' href='/favicon.png'>
-   <link rel='stylesheet' href='/global.css'>
    <link rel='stylesheet' href='/build/bundle.css'>

    <script defer src='/build/bundle.js'></script>



add svelte-windicss-preprocess config to rollup.config.js

  import svelte from 'rollup-plugin-svelte';
  import commonjs from '@rollup/plugin-commonjs';
  import resolve from '@rollup/plugin-node-resolve';
  import livereload from 'rollup-plugin-livereload';
  import { terser } from 'rollup-plugin-terser';
  import css from 'rollup-plugin-css-only';
+ import { windi } from 'svelte-windicss-preprocess';

  const production = !process.env.ROLLUP_WATCH;

  function serve() {
    let server;

    function toExit() {
      if (server) server.kill(0);

    return {
      writeBundle() {
        if (server) return;
        server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
          stdio: ['ignore', 'inherit', 'inherit'],
          shell: true

        process.on('SIGTERM', toExit);
        process.on('exit', toExit);

  export default {
    input: 'src/main.js',
    output: {
      sourcemap: true,
      format: 'iife',
      name: 'app',
      file: 'public/build/bundle.js'
    plugins: [
+       preprocess: [
+         windi({}),
+       ],
        compilerOptions: {
          // enable run-time checks when not in production
          dev: !production
      // we'll extract any component CSS out into
      // a separate file - better for performance
      css({ output: 'bundle.css' }),

      // If you have external dependencies installed from
      // npm, you'll most likely need these plugins. In
      // some cases you'll need additional configuration -
      // consult the documentation for details:
        browser: true,
        dedupe: ['svelte']

      // In dev mode, call `npm run start` once
      // the bundle has been generated
      !production && serve(),

      // Watch the `public` directory and refresh the
      // browser on changes when not in production
      !production && livereload('public'),

      // If we're building for production (npm run build
      // instead of npm run dev), minify
      production && terser()
    watch: {
      clearScreen: false

update App.svelte

    export let name;

    <h1>Hello {name}!</h1>
    <p>Visit the <a href="">Svelte tutorial</a> to learn how to build Svelte apps.</p>

+ <style windi:preflights:global windi:safelist:global>
+ </style>
- <style>
-   main {
-     text-align: center;
-     padding: 1em;
-     max-width: 240px;
-     margin: 0 auto;
-   }
-   h1 {
-     color: #ff3e00;
-     text-transform: uppercase;
-     font-size: 4em;
-     font-weight: 100;
-   }
-   @media (min-width: 640px) {
-    main {
-       max-width: none;
-     }
-   }
- </style>


Install the Svelte WindiCSS Preprocessor plugin from NPM

npm i -D svelte-windicss-preprocess

add svelte-windicss-preprocess config to svelte.config.js

+ import { windi } from "svelte-windicss-preprocess";
  /** @type {import('@sveltejs/kit').Config} */
  const config = {
+   preprocess: [
+     windi({})
+   ],
    kit: {
      // hydrate the <div id="svelte"> element in src/app.html
      target: '#svelte'

export default config;

add layout file

+ ./src/routes/__layout.svelte
+ <nav>
+   <a href=".">Home</a>
+   <a href="about">About</a>
+   <a href="settings">Settings</a>
+ </nav>

+ <slot></slot>
+ <style windi:preflights:global windi:safelist:global>
+ </style>



interface Options {
  silent?: boolean
  mode?: 'development' | 'production'
  configPath?: string
  disableFormat?: boolean
  devTools?: {
    enabled: boolean
    completions?: boolean
  safeList?: string
  preflights?: boolean

Custom pre-processing information

With v4 we introduced a new feature set of custom attributes for svelte style tags, to make integration regardless the setup easy and straight forward. Non scoped styles will be handled in svelte according to their docs with :global() for classes and -global- for keyframes.


Svelte nature is to scope CSS style and remove unused styles, this can lead to issues if you add preflights into the layout wrapper and want that styles to be available on all other .svelte files as well. On the other hand, if you compile to custom-elements you cannot use :global() styles. To allow the user to decide where to put the preflights as well deciding if they should be global or scoped, we have following syntax:

<!-- Layout.svelte -->

<slot />

<!-- use this for scoped preflights -->
<style windi:preflights>

<!-- use this for global preflights -->
<style windi:preflights:global>

Safe list

Sometimes you want to have dynamic classes based on some logic in script tags. Since svelte-windicss-preprocess runs before the svelte compile step, there is no way it could know this dynamic values. There are many approaches to this, either use windi at runtime or using a bundler setup instead this preprocessor one, or if you know all possible classes in the beginning add them to a safe list.

Similar to preflights, this safe list need to be available anywhere you want it, and also scoped and global.

<!-- Layout.svelte -->
  let shade = 100;

<div class="bg-red-{shade}">
  I am dynamic!

<!-- use this for scoped safelist classes -->
<style windi:safelist>

<!-- use this for global safelist classes -->
<style windi:safelist:global>

Windi CSS classes

By default, all inline used classes of Windi CSS will be scoped with native svelte logic. This has its advantages (you can find many discussions online). However, using a utility based CSS framework there is not much need to make sure classes do not override, since e.g. bg-gray-600 will always have the same CSS code behind it, regardless which .svelte file it is used. You might want to safe more file size and using Windi CSS classes not scoped, but might want to choose this file by file.

To make all Windi CSS classes in one .svelte global, with help of :global() you can modify / add the following style tag.

<style windi:global>

Custom styles

You may have the need in your project to define custom CSS classes, and want to decide separate to Windi CSS if they are scoped or global. You can with the following syntax:

  <!-- all styles with :global() -->
  <style global>
    .btn {
      background: green;

  <!-- selective or all scoped -->
    :global(.btn) {
      background: green;
    .btnTwo {
      background: red;

You can combine any of this attributes, so full style tag can look like:

  <style global windi:global windi:preflights:global windi:safelist:global>
      background: black;

VS Code Extension

Using special CSS tag syntax as well as the attributes above, will break the CSS diagnostics of VS Code. Please make sure to disable them. If you are using Svelte for VS Code, add this setting to your VS Code configuration file.

  "svelte.plugin.css.diagnostics.enable": false
Windi CSS is Sunsetting We recommend new projects to consider alternatives. Click for more information.