DEV Community

Ilia Mikhailov
Ilia Mikhailov

Posted on • Updated on • Originally published at codechips.me

Svelte + Tailwind + Parcel = Awesome!

I must admit that Rollup.js, that default Svelte projects use, never grew on me for some reason so I decided to give Parcel a try. Let's see how to setup a new Svelte project using it. While on it we will also include some other useful tools and plugins on the way.

Basic Basics

Let's start by creating a simple new project with Yarn.

$ mkdir -p app/src && cd app
$ yarn init -y
Enter fullscreen mode Exit fullscreen mode

Add Parcel, Svelte and required plugins.

$ yarn add -D parcel-bundler svelte parcel-plugin-svelte

Enter fullscreen mode Exit fullscreen mode

We are almost ready to go, but before that we need to add some stuff to package.json and some actual source files. Start by adding the following properties to your package.json.

  "scripts": {
    "start": "parcel src/index.html --port 3000",
    "build": "rm -rf dist && parcel build src/index.html --no-source-maps"
  },
  "browserslist": [
    "last 1 chrome versions"
  ]

Enter fullscreen mode Exit fullscreen mode

Our server will listen on port 3000 and when building production bundle we will skip source map generation.

Now, let's add some actual source files.

src/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>My App</title>
  </head>

  <body>
    <script defer src="./main.js"></script>
    <noscript>You need to enable JavaScript to run this app.</noscript>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

src/main.js

import App from './App.svelte';

const app = new App({
  target: document.body
});

export default app;

Enter fullscreen mode Exit fullscreen mode

src/App.svelte

<script>
  let name = "friend";
</script>

<h1>Hello {name}!</h1>

Enter fullscreen mode Exit fullscreen mode

We are now ready to start our app.

$ yarn start
yarn run v1.21.1
$ parcel src/index.html --port 3000
Server running at http://localhost:3000
✨  Built in 923ms.
Enter fullscreen mode Exit fullscreen mode

Wow! How awesome is that? And really fast too! Try changing some text in App.svelte and see how fast everything recompiles.

We can also build a production bundle.


$ yarn build
yarn run v1.21.1
$ rm -rf dist && parcel build src/index.html --no-source-maps
✨  Built in 1.22s.

dist/main.a3795f1f.js    22.92 KB    895ms
dist/index.html             347 B    279ms
Done in 1.76s.

Enter fullscreen mode Exit fullscreen mode

Just look at how fast the build is! Amazing!

Intermediate Basics

Let's install Tailwind - a functional CSS framework and make it play nice with our current setup.

$ yarn add -D tailwindcss autoprefixer @fullhuman/postcss-purgecss
Enter fullscreen mode Exit fullscreen mode

We have to add some additional files for Tailwind to work.

Create Tailwind config file.

$ yarn tailwind init

Enter fullscreen mode Exit fullscreen mode

Create base styles file - src/global.pcss with the following content.

@tailwind base;
@tailwind components;
@tailwind utilities;

Enter fullscreen mode Exit fullscreen mode

Create a PostCSS config file - postcss.config.js.

const plugins =
  process.env.NODE_ENV === 'production'
    ? ['tailwindcss', 'autoprefixer', '@fullhuman/postcss-purgecss']
    : ['tailwindcss'];

module.exports = { plugins };

Enter fullscreen mode Exit fullscreen mode

The config purges unused CSS and adds browser prefixes only in production builds. Why? Because during development you want to have a full Tailwind CSS file so you can tinker with various classes in your browser's dev console.

Finally, let's add a PurgeCSS config so that PostCSS will know what unused CSS to purge during the production builds. Create a purgecss.config.js file with the following content.

module.exports = {
  content: [
    './src/index.html',
    './src/**/*.svelte'
  ],
  whitelistPatterns: [/svelte-/],
  defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
};

Enter fullscreen mode Exit fullscreen mode

Here we are telling it to ignore svelte- classes when purging. These are classes that Svelte generates whey you write scoped styles in you Svelte components.

We could have wired up our configuration only in postcss.config.js, but I think it's nicer to have them in two different files for clear separation of concerns.

Finally, include the following line in the head tag in index.html.

<link rel="stylesheet" href="./global.pcss" />

Enter fullscreen mode Exit fullscreen mode

Add the following classes to the H1 tag in App.svelte to see that everything works as expected.

<h1 class="text-teal-700 text-5xl">Hello {name}!</h1>

Enter fullscreen mode Exit fullscreen mode

Boom! If you now start the app you should see a styled heading. NOTE: If for some reason it doesn't work, delete Parcel's .cache folder and restart the app.

So there you go. New fresh and slick Svelte setup. You can stop here and go build your next great thing or you can continue reading and maybe learn something new.

Bonus Basics

Inter font is pretty sweet if you are building UIs. Here is how to include it in our new setup with tailwindcss-font-inter plugin.

$ yarn add -D tailwindcss-font-inter

Enter fullscreen mode Exit fullscreen mode

Replace tailwind.config.js with following content.

module.exports = {
  theme: {
    interFontFeatures: {
      default: ['calt', 'liga', 'kern'],
      numeric: ['tnum', 'salt', 'ss02']
    },
    fontSize: {
      xs: '0.75rem',
      sm: '0.875rem',
      base: '1rem',
      lg: '1.125rem',
      xl: '1.25rem',
      '2xl': '1.5rem',
      '3xl': '1.875rem',
      '4xl': '2.25rem',
      '5xl': '3rem',
      '6xl': '4rem',
      '7xl': '6rem',
      '8xl': '8rem',
      '9xl': '9rem',
      '10xl': '10rem'
    },
    extend: {}
  },
  variants: {},
  plugins: [
    require('tailwindcss-font-inter')({
      importFontFace: true,
      disableUnusedFeatures: true
    })
  ]
};

Enter fullscreen mode Exit fullscreen mode

Now add the following class to the body tag in index.html.

<body class="font-inter">

Enter fullscreen mode Exit fullscreen mode

Your app should now use the Inter font. There are many configuration options for how the font is rendered. Let that be a home assignment.

Extra Bonus Basics

Linting and code formatting is important. I personally use Vim with coc.vim's Svelte extension when writing code. It's actually working mighty fine I must say! However, in order for that to work you have to install some additional plugins and add more configs.

$  yarn add -D prettier prettier-plugin-svelte eslint eslint-plugin-svelte3

Enter fullscreen mode Exit fullscreen mode

Add .prettierrc.json

{
  "tabWidth": 2,
  "semi": true,
  "singleQuote": true,
  "plugins": ["prettier-plugin-svelte"],
  "svelteSortOrder": "styles-scripts-markup",
  "svelteStrictMode": true,
  "svelteBracketNewLine": true,
  "svelteAllowShorthand": true
}
Enter fullscreen mode Exit fullscreen mode

And .eslintrc.json

{
  "env": {
    "browser": true,
    "es6": true
  },
  "parserOptions": {
    "ecmaVersion": 2019,
    "sourceType": "module"
  },
  "plugins": ["svelte3"],
  "extends": ["eslint:recommended"],
  "overrides": [
    {
      "files": ["**/*.svelte"],
      "processor": "svelte3/svelte3"
    }
  ],
  "rules": {
    "prettier/prettier": "error",
    "svelte3/lint-template": 2
  }
}
Enter fullscreen mode Exit fullscreen mode

Voila! Linting and code formatting should now work.

Conclusion

As you see it's not hard to use Svelte with Parcel. The setup feels very fresh and snappy. Everything with Parcel works almost out-of-the-box. I really like it.

If you want to learn and understand how things are wired together you can repeat all the steps above to understand what's happening, but if you are lazy (like me) and just want to bang out some code, you can use my boilerplate and be done with it.

$ npx degit codechips/svelte-tailwind-parcel-starter facebook-killer

Enter fullscreen mode Exit fullscreen mode

Thanks for reading and happy coding!

Latest comments (11)

Collapse
 
jackedai profile image
jackedai

Signed up just to say thank you, this cut my build time from 15 seconds with rollup to 130ms. Really appreciate it thankyou sir!

Collapse
 
triptych profile image
Andrew Wooldridge

fyi on windows you have to use a different method to delete the dist dir:
"build2": "rmdir /s /q dist && parcel build src/index.html --no-source-maps"

Collapse
 
codechips profile image
Ilia Mikhailov

Yep. Thanks! Maybe the best is to use some kind of cross-platform lib like del-cli.

Collapse
 
netaisllc profile image
CSSian

My terminal reports: Built in 37ms.
I give you the credit!
Thanks for doing the working putting this together.

Collapse
 
parables profile image
Parables

I followed your steps to create the Svelte template only. I checked the size and it was over 70.4 MB whiles the default one with Rollup is just about 16.5 MB . Why is this so ?

Collapse
 
parables profile image
Parables

You know what, am feed up with Rollup, I am finding it hard to use ES6 export and import syntax but I just read that ". Parcel supports both CommonJS and ES6 module syntax for importing files" so am gonna give it a try and forget about Rollup

Collapse
 
felipesere profile image
Felipe Sere

👋 interesting article. Have you looked at the testing side of Svelte?

Collapse
 
codechips profile image
Ilia Mikhailov

Haven't actually looked into component testing yet. I usually prefer e2e testing for web apps and I use tdd for separate files such as stores, services, etc with ava.js.

Collapse
 
codechips profile image
Ilia Mikhailov

Thanks! How often do change your config file? PostCSS imports are possible with Tailwind. You just have to structure your files in specific way. tailwindcss.com/docs/using-with-pr.... I think you should give Parcel and Tailwind a second chance :)

Collapse
 
khrome83 profile image
Zane Milakovic

I really love this. But I need static site generation and have to use sapper.

I much prefer parcel over Webpack or rollup for simple spas and flat file sites.

This is a really good write up. Thank you for sharing.

Collapse
 
codechips profile image
Ilia Mikhailov

Hear hear. Sapper is a different beast. Thanks for reading!