DEV Community

loading...

How to Set Up Svelte with Tailwind CSS

swyx profile image swyx Originally published at swyx.io Updated on ・4 min read

Mar 2021 update: SvelteKit and Tailwind JIT were released this month and completely changed the game. Both are still in beta but strongly recommend checking out this template and this blogpost and this CLI!

npx svelte-add tailwindcss  --jit
Enter fullscreen mode Exit fullscreen mode

Jan 2021 update: minor update of config for Tailwind 2.0. I've also added alternative approaches at the bottom, scroll down

Sep 2020 update: Chris has shown a much easier way to do this than I originally outlined, so I have replaced my original notes with notes from his sample repo. There's a build speed tradeoff, I discuss alternatives at bottom.

On the latest Toolsday, Chris Dhanaraj said he had trouble finding documentation for adding Tailwind to Svelte.

Today I also needed to add Tailwind to a Svelte project, so I am writing this as a reference for myself. Setting up PostCSS with Svelte is something I have documented on the new Svelte Society site, but of course it could be better and more specifically tailored to Tailwind (which after all is "just" a PostCSS plugin).

So I am writing this for him and for me.

A quick aside on WHY Use Tailwind with Svelte, since Svelte offers scoped CSS by default: Tailwind offers a nicely constrained "design system" so you don't overuse Magic Numbers and it's easy to add responsive styling with Tailwind breakpoints. Because Tailwind has the developer experience of "inline styles", I also find it easier to delete and move HTML around without having to go back for the styling. I also like not having to name classes. I discuss more on Why Tailwind in general in a separate post.

3 Steps

I will assume you have a standard existing Svelte or Sapper project with no PostCSS/Tailwind set up. I'll also add in autoprefixer and postcss-nesting since I like to work with those, but of course feel free to remove as needed.

Step 1: Install deps

npm install -D svelte-preprocess tailwindcss autoprefixer postcss-nesting

# optional tailwind ui plugin
npm install @tailwindcss/ui
Enter fullscreen mode Exit fullscreen mode

Step 2: Setup Config Files

Add a tailwind.config.js file at the project root:

// tailwind.config.js
const isProduction = !process.env.ROLLUP_WATCH; // or some other env var like NODE_ENV
module.exports = {
  // only needed in Tailwind 1.0 for tailwind 2.0 compat
  // future: { 
  //     purgeLayersByDefault: true, 
  //     removeDeprecatedGapUtilities: true,
  //   },
  plugins: [
    // for tailwind UI users only
    require('@tailwindcss/ui'),
    // other plugins here
  ],
  purge: {
    content: [
      "./src/**/*.svelte",
      // may also want to include HTML files
      // "./src/**/*.html"
    ], 
    // this is for extracting Svelte `class:` syntax but is not perfect yet, see below
    defaultExtractor: content => {
      const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || []
      const broadMatchesWithoutTrailingSlash = broadMatches.map(match => _.trimEnd(match, '\\'))
      const matches = broadMatches
        .concat(broadMatchesWithoutTrailingSlash)
      return matches
    },
    enabled: isProduction // disable purge in dev
  },
};
Enter fullscreen mode Exit fullscreen mode

And now set it up inside of your Svelte bundler config as well:

import sveltePreprocess from "svelte-preprocess";

const production = !process.env.ROLLUP_WATCH;

export default {
  plugins: [
    svelte({
      // etc...
      preprocess: sveltePreprocess({
        // https://github.com/kaisermann/svelte-preprocess/#user-content-options
        sourceMap: !production,
        postcss: {
          plugins: [
             require("tailwindcss"), 
             require("autoprefixer"),
             require("postcss-nesting")
          ],
        },
      }),
    }),
  ]
}
Enter fullscreen mode Exit fullscreen mode

Here is the equivalent for Svelte with Webpack:

// webpack.config.js
const sveltePreprocess = require('svelte-preprocess');
module.exports = {
  // ...
    module: {
        rules: [
            {
                test: /\.svelte$/,
                use: {
                    loader: 'svelte-loader',
                    options: {
                        emitCss: true,
                        hotReload: true,
                        preprocess: sveltePreprocess({
                            // https://github.com/kaisermann/svelte-preprocess/#user-content-options
                            sourceMap: !prod,
                            postcss: {
                                plugins: [
                                    require("tailwindcss"), 
                                    // require("autoprefixer"),
                                    require("postcss-nesting")
                                ],
                            },
                        }),
                    }
                }
            },
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Add the Tailwind includes to your Svelte App

Typically a Svelte app will have a way to inject css already, so all we do is piggyback onto that. You'll want to put these includes at a reasonably high level, say App.svelte or Layout.svelte component that will be included in every page of your site.

<style global lang="postcss">

  /* only apply purgecss on utilities, per Tailwind docs */
  /* purgecss start ignore */
  @tailwind base;
  @tailwind components;
  /* purgecss end ignore */

  @tailwind utilities;

</style>
Enter fullscreen mode Exit fullscreen mode

And that's it!

Note: this section used to involve messing with package.json scripts to run postcss-cli, but Chris realized that you didn't need to do any of this since Svelte already has a way to inject CSS and svelte-preprocess already runs on every Svelte file.

Please see:

To see this working in action.

Unresolved

Svelte has a class: binding syntax that isnt' supported by Tailwind out of the box. There is an open discussion for this.

Alternative Approaches

This method outlined above is simple to get running, but does end up running thousands of lines of Tailwind's CSS through the Svelte compiler. This may cause performance issues (primarily, every time you change the entry point file). Alternative approaches may be more appropriate depending on your preferences:

Discussion (22)

pic
Editor guide
Collapse
dctalbot profile image
David Talbot

I followed these steps, but had major perf issues. Anyone else find the sveltePreprocess step to be very slow? Every change I made was taking around 18 seconds to reload the page. I sped it up by disabling source maps and purging, but it still took 11 seconds. I ended up rolling my own setup and now it takes 109ms. Still don't really understand what was going on, but I'm happy with my setup so I saved it here: github.com/dctalbot/svelte-typescr...
Cheers!

Collapse
khromov profile image
Stanislav Khromov

Hello and thank you for writing this tutorial!

I've followed along with it but ran into an issue - the resulting bundle.css does not have prefixed classes. It seems like PostCSS does not prefix them, unlike if you use a normal <style> tag where Svelte will prefix the classes.

I've added the prefix: 'my-prefix' setting to tailwind.config.js. It does work, but you have to write out the prefixed version in your code, so flex becomes my-prefix-flex, and so on. This gets old fast. 😅

In an ideal world, I would write the normal Tailwind classes and the resulting bundle would be automatically prefixed.

PS.
I've also tried the postcss-prefixer package, but then Svelte didn't include any of my classes in the bundle.css output. Perhaps i misconfigured it...

Thank you so much again and hope that you or someone else has solved this issue or have some suggestions for how to proceed!

Collapse
swyx profile image
swyx Author

no idea about that, sorry. I don't use any prefixes. perhaps check one of the alternative methods listed at the bottom to see if works

Collapse
mgrisole profile image
mgrisole

In my case I had to change the build script from:

"build": "rollup -c"
to
"build": "NODE_ENV=production rollup -c"

to actually purge unused css

Ref: tailwindcss.com/docs/optimizing-fo...

Collapse
sarioglu profile image
sarioglu

Here is another similar tutorial for Svelte and Sapper that I have written a while ago: dev.to/sarioglu/using-svelte-with-...

It also includes template repositories for both.

Collapse
swyx profile image
swyx Author

added, cheers. i think mine is a bit simpler tho

Collapse
rraihansaputra profile image
raihan saputra

Hi Shawn, I'm trying out this config with the routify-ts template. I got the classes working, but seems like @apply directives doesn't work on my project. Did you get this working with this config?

Collapse
swyx profile image
swyx Author

it worked for me but I don't use Routify so I don't know how to help you

Collapse
rraihansaputra profile image
raihan saputra

Managed to get it working using your config. There was a duplicate preprocess key on my rollup config. Thank you for the write up!

Collapse
dwstevens profile image
David Stevens

@swyx Have you seen WindiCSS? It seems to fix the overhead on the preprocess side. windicss.netlify.app/guide/svelte....

Collapse
malikbenkirane profile image
Malik Benkirane

Thank you! I'm going to try that right away

Collapse
malikbenkirane profile image
Malik Benkirane • Edited

Although the project is very promising...
I have discovered some issues there with an {#each...} inside of an {#if...}
I have to dig into that but I don't get compiled class for this one and styles won't get applied.

Collapse
codechips profile image
Ilia Mikhailov

In case anyone needs to setup Sapper + Tailwind one day 🙂

sapper-with-postcss-and-tailwind.v...

Collapse
swyx profile image
swyx Author

added, cheers

Collapse
paul42 profile image
Paul.42

just curious, why the purge css ignore? don't you want purge CSS to remove unused styles from the base, or is purge css too aggressive?
Thanks for the article!

Collapse
swyx profile image
swyx Author

no reason - i'll be honest i blindly copied it from github.com/chrisdhanaraj/svelte-ta... but you're right, it's not needed, i took it out

Collapse
chrisdhanaraj profile image
Chris Dhanaraj

For context, I pulled that one from Tailwind's docs on Controlling File Size.

tailwindcss.com/docs/controlling-f...

Thread Thread
swyx profile image
swyx Author

dammit, lol nicely done. will add back with a note

Collapse
giorgosk profile image
Giorgos Kontopoulos 👀

Nice one @swyx perhaps you can add some tags #svelte #tailwind ?

Collapse
swyx profile image
swyx Author

oh word lol

Collapse
kevinast profile image
Kevin Bridges

Thanks for this reference @swyx . It proved to be invaluable for a new tailwind svelter!

BTW: You may want to mention that most likely you will want to remove the public/global.css reference (of the svelte template) so it does not interfere with tailwind css. This may be obvious to some, but I had several issues related to this.

Collapse
jesuscovam profile image