How to Set Up SvelteKit with Tailwind CSS
Tags: svelte, tailwindcss, javascript
Published: 2020-09-08 16:30:58

---
title: How to Set Up SvelteKit with Tailwind CSS
published: true
description: A quick 3 step guide for myself on how to set up Svelte with Tailwind CSS
tags: Svelte, TailwindCSS, JavaScript
slug: svelte_tailwind_setup
canonical_url: https://www.swyx.io/writing/svelte_tailwind_setup
---

> Jun 2021 update: This guide has been completely updated for SvelteKit@next.113 + Tailwind JIT! You can also see [this guide](https://jsco.dev/blog/how-to-set-up-tailwindcss-with-svelte-kit)

> Aug 2021 update: try this first:
>
> ```bash
> npm init svelte@next
> npx svelte-add@latest tailwindcss
> npm install
> ```

## Prerequisites

I'm going to skip the preamble. You can read:

- Why Svelte
  - [Why I Enjoy Svelte](https://www.swyx.io/svelte-why/)
  - [Svelte for Sites, React for Apps](https://www.swyx.io/svelte-sites-react-apps/)
- [Why Tailwind CSS](https://www.swyx.io/why-tailwind/)

I will also assume you have SvelteKit set up:

```bash
npm init svelte@next my-app
cd my-app
npm install
npm run dev
```

I strongly recommend picking at least the ESLint and Prettier options.

## Step 1: Install deps

```bash
npm install -D svelte-preprocess tailwindcss autoprefixer postcss

# optional tailwind ui plugin
npm install @tailwindcss/ui
```

### Step 2: Setup Config Files

Add a `tailwind.config.cjs` file at the project root. (we use `.cjs` for now, or else the tailwind vs code extension complains it uses `require` when the rest of the project is in ESM).

```js
// tailwind.config.cjs
module.exports = {
    mode: 'jit',
    // you dont need `purge: enabled: production` because you are using jit
    purge: [ 
      "./src/**/*.svelte",
      // may also want to include HTML files
      "./src/**/*.html"
    ],
    darkMode: 'class',
    theme: {
      extend: {},
    },
    variants: {},
    plugins: [],
  }
```

## Step 2A: Svelte Config with inline PostCSS

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

```js
// svelte.config.js
import preprocess from 'svelte-preprocess';
import tailwind from 'tailwindcss'
import autoprefixer from 'autoprefixer'

/** @type {import('@sveltejs/kit').Config} */
const config = {
	// Consult https://github.com/sveltejs/svelte-preprocess
	// for more information about preprocessors
	preprocess: preprocess({
		postcss: {
			plugins: [
				tailwind, 
				autoprefixer
			]
		}
	}),

	kit: {
		// hydrate the <div id="svelte"> element in src/app.html
		target: '#svelte'
	}
};

export default config;
```

## Step 2B: Svelte Config with separate PostCSS for PostCSS nesting

**ALTERNATIVELY** you can also set up your `postcss.config.js` in a separate file (this seems to be better for the postcss nesting plugin to run).

We use PostCSS 7, not 8 (latest) for now, because... [JavaScript](https://stackoverflow.com/questions/64057023/error-postcss-plugin-autoprefixer-requires-postcss-8-update-postcss-or-downgra). I've added in postcss-nesting because I like it - feel free to remove of course.

- `npm i -D postcss-load-config postcss@7 postcss-nesting@7`
- `svelte.config.js`:
  ```js
	preprocess: preprocess({
		defaults: {
			style: 'postcss',
		},
		postcss: true,
	}),
   ```
- `postcss.config.js`

```js

import tailwind from 'tailwindcss'
import autoprefixer from 'autoprefixer'
import nesting from 'postcss-nesting'

export default  {
	syntax: 'postcss-scss',
	plugins: [
		// Some plugins, like postcss-nested, need to run before Tailwind
		nesting(),
		tailwind(),

		// But others, like autoprefixer, need to run after

		autoprefixer(),
		// !dev &&
		// 	cssnano({
		// 		preset: 'default',
		// 	}),
	],
};
```

However, this setup is still buggy when you actually try to nest CSS. I haven't figured it out yet and would like some help here if you are reading this.

### Step 3: Add the Tailwind includes to your Svelte App

Create `src/routes/__layout.svelte` and add this:

```html
<style global lang="postcss">
	@tailwind base;
	@tailwind components;
	@tailwind utilities;
</style>

<slot></slot>
```

And that's it!

## Optional: Dark Mode

I like using Dark mode with classes so you can let the user toggle it:

Put this in your `app.html`:

```html
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<link rel="icon" href="/favicon.png" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<link href="https://fonts.googleapis.com/css2?family=Varela+Round&display=swap" rel="stylesheet">
		<script>
			// On page load or when changing themes, best to add inline in `head` to avoid FOUC
			if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
			document.documentElement.classList.add('dark')
			} else {
			document.documentElement.classList.remove('dark')
			}
		</script>
		%svelte.head%
	</head>
	<body class="dark:bg-gray-900 dark:text-gray-100">
		<div id="svelte">%svelte.body%</div>
	</body>
</html>
```

## What about Svelte classes!

Svelte has a `class:` binding syntax that [didn't used to be supported by Tailwind](https://github.com/tailwindlabs/tailwindcss/discussions/1731). With Tailwind JIT, it has now been supported [as of 2021](https://github.com/tailwindlabs/tailwindcss-jit/pull/183)!

## What versions are you working with?!?

```js
  "devDependencies": {
    "@sveltejs/adapter-netlify": "^1.0.0-next.17",
    "@sveltejs/kit": "^1.0.0-next.113",
    "@typescript-eslint/eslint-plugin": "^4.19.0",
    "@typescript-eslint/parser": "^4.19.0",
    "autoprefixer": "^9.8.6",
    "eslint": "^7.22.0",
    "eslint-config-prettier": "^8.1.0",
    "eslint-plugin-svelte3": "^3.2.0",
    "postcss": "^8.3.0",
    "postcss-load-config": "^3.0.1",
    "prettier": "~2.2.1",
    "prettier-plugin-svelte": "^2.2.0",
    "svelte": "^3.34.0",
    "svelte-check": "^2.0.0",
    "svelte-preprocess": "^4.7.3",
    "tailwindcss": "^2.1.4",
    "tslib": "^2.0.0",
    "typescript": "^4.0.0"
  },
```

## Reference Repos

More practical repos you can reference and run

- https://github.com/sw-yx/swyxkit
- https://github.com/navneetsharmaui/sveltekit-starter
- https://github.com/Dan1ve/Sveltekit-Typescript-TailwindCSS-Jit

You are supposed to be able to run [`npx svelte-add tailwindcss  --jit`](https://github.com/svelte-add/tailwindcss) - but as of time of writing it is very buggy and doesn't work.


---

## Appendix: Prior content (Old outdated content)

> 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 these templates and tools: 

- https://github.com/navneetsharmaui/sveltekit-starter
- https://github.com/Dan1ve/Sveltekit-Typescript-TailwindCSS-Jit
- more integrations here: https://github.com/sveltejs/integrations#sveltekit-templates
- `npx svelte-add tailwindcss  --jit` 
 https://github.com/svelte-add/tailwindcss

> 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](https://github.com/chrisdhanaraj/test-svelte-tailwind). There's a build speed tradeoff, I discuss alternatives at bottom. 


On [the latest Toolsday](https://spec.fm/podcasts/toolsday/o2XubpwD), [Chris Dhanaraj](https://twitter.com/chrisdhanaraj) said he had trouble finding documentation for adding [Tailwind](https://tailwindcss.com/) to [Svelte](https://svelte.dev/). 

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](https://sveltesociety.dev/recipes/build-setup/using-postcss-with-svelte), 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](https://css-tricks.com/magic-numbers-in-css) and it's easy to add responsive styling with [Tailwind breakpoints](https://tailwindcss.com/docs/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](https://www.swyx.io/writing/how-to-name-things/#not-naming-things-aug-2019-edit). I discuss more on [Why Tailwind in general](https://dev.to/swyx/why-tailwind-css-2o8f) in a separate post. 

 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`](https://tailwindcss.com/docs/using-with-preprocessors#nesting) since I like to work with those, but of course feel free to remove as needed.


> 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:

- [Chris' sample Svelte repo](https://github.com/chrisdhanaraj/test-svelte-tailwind)
- [Chris' Sapper side project](https://github.com/chrisdhanaraj/tier-list/)

To see this working in action.


## 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:

- David Parker's video on [Adding TailwindCSS to a Sapper / Svelte App](https://www.youtube.com/watch?v=Dvm25Wgoh8Q)
- Jacob Babich: "I'm moving to https://github.com/babichjacob/sapper-postcss-template/tree/main running the global css builder in parallel with a reimplementation of postcss-cli (just so I can have source maps controlled by a variable in rollup.config.js) but without getting that extreme you can just use [npm-run-all](http://npm.im/npm-run-all) with [postcss-cli](https://www.npmjs.com/package/postcss-cli)"
- dominikg: "The easiest way to setup tailwind with svelte: `npx svite create -t postcss-tailwind my-svelte-tailwind-project`"
- https://github.com/sarioglu/sapper-tailwindcss-template
- https://github.com/sarioglu/svelte-tailwindcss-template
- https://github.com/breadthe/svelte-tailwind2-starter
- https://codechips.me/sapper-with-postcss-and-tailwind/