SvelteJS with Tailwind and Typescript

Svelte js with tailwind and typescript

Create Starter app

To setup Svelte I open a terminal and use the command from the official Svelte homepage. TypeScript support has been already added to this template, so nothing special here.

npx degit sveltejs/template my-svelte-project
# or download and extract 
cd my-svelte-project

Enable TypeScript

  • enable typescript support
node scripts/setupTypeScript.js

install deps and start application

# install npm dependencies
npm i
# run dev server
npm run dev

Install Tailwind

Back in the Terminal I add Tailwind as described in their documentation.

npm install -D tailwindcss@latest postcss@latest
# After this step I generate a default tailwind.config.js file with

npx tailwindcss init
# If you prefer a full Tailwind config use the --full argument:
npm tailwindcss init --full

See the Tailwind documentation for more infos about this topic.

Configure Rollup to use Postcss

The default Svelte template uses Rollup as a bundler. When I run the setupTypeScript.js from the first setup step, I get the famous svelte-preprocess plugin already integrated into the rollup setup. The only thing left is that I add the config for postcss as options to the svelte-preprocess plugin. Here are the changes that I make in rollup.config.js:

// rollup.config.js (partial)
export default {
  plugins: [
       preprocess: sveltePreprocess({
         postcss: {
           plugins: [require("tailwindcss")],

At this point Rollup should trigger postcss and therefore the Tailwind plugin. To enable it in my application, I still need one important step.

Adding a Tailwind Component to the App

Now it's time to create a Svelte component that contains the postcss to generate all the classes. I call mine Tailwind.svelte but the name doesn't really matter.

// src/Tailwind.svelte
<style global lang="postcss">
  @tailwind base;
  @tailwind components;
  @tailwind utilities;

Some things to note here:

The component only has a single style element with no markup. The attribute global tells the svelte-preprocess plugin to not scope the css to this component. Remember by default Svelte scopes every css to the component it was declared, in this case I don't want this. The lang="postcss" attribute is telling svelte-preprocess to use postcss for the content. As a goody, some IDE extensions now display the content with the correct syntax highlighting for postcss. Now use the Tailwind component in src/App.svelte

// src/App.svelte
<script lang="ts">
  import Tailwind from "./Tailwind.svelte";

<Tailwind />
<div class="bg-gray-200 px-4 py-2 rounded">Hello Tailwind!</div>

Now my browser displays a Tailwind styled div. Very nice! Let's clean up the public/index.html and remove the global.css link tag and remove the corresponding file from public/global.css I don't use it.

<!-- public/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='/build/bundle.css'>

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

Let's finish the setup for production builds. Right now it's perfect for development. I can use any Tailwind class and except for the first start of the development server, where all the Tailwind classes get generated, it behaves very snappy on rebuilds.

Production Builds

When it comes to production builds, right now I have not configured anything so I'll get a bundle.css with all Tailwind classes. I don't want that for a production build, so I modify the tailwind.conf.js to use it's integrated purgecss for that purpose.

// tailwind.config.js
module.exports = {
  purge: ["src/**/*.svelte", "public/index.html"],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  variants: {
    extend: {},
  plugins: [],

With this modification Tailwind removes all classes that are not used in .svelte files and in the public/index.html html file. I added the public/index.html file because sometimes I add containers or some responsive design utilities directly on the tag. If you don't need this, you can remove the index.html file from the purge list, or add additional files I don't have listed here. For example: if I use some plugins that contain .js, .ts, .html, ... files that use Tailwind classes, I would add them to this purge array too.

There is one little detail about the Tailwind purge: it only is executed if NODE_ENV=production which makes sense. I set this environment directly in my package.json scripts:

// package.json (partial)
  "scripts": {
      "build": "NODE_ENV=production rollup -c",

With these settings my bundle.css only contains the Tailwind classed I really use, plus the mandatory css reset code that Tailwind provides.


One last thing to add for production is vendor prefixes. I usually go with the defaults and just add autoprefixer as postcss plugin. If you need more control, add configuration as you please.

# Install autoprefixer with npm:

npm i -D autoprefixer

Add it as postcss plugin in rollup.config.js:

// rollup.config.js (partial)
  preprocess: sveltePreprocess({
    postcss: {
      plugins: [

That's it.