Svelte.js for Vue People

Published April 30th, 2019 2m read time

Don't get me wrong—I love Vue. But I can't help getting excited by shining new things (frameworks). I saw Rich Harris' "Rethinking Reactivity" talk on YouTube and remembered hearing him on Shop Talk. I figured I'd give Svelte.js a try. Svelte's big thing is that it only works as a compiler, requiring no inclusion of library code (mostly, anyway) like Vue or React. I'm a minimalist at heart, so this idea really appealed to me. Svelte even has its own Nuxt-style framework built on top of Svelte called Sapper. I decided to start there.

The biggest issue as of now (April 30, 2019) is that Sapper's master branch hasn't updated along with the recent release of Svelte 3. There are Webpack and Rollup templates for v3, but you'll have to dig for them. The Webpack one has an error whenever hot module reloading is turned on, and I could never get Rollup to work with PostCSS. There's a Parcel plugin for Svelte as well, but that isn't working with v3 right now either. If you can do without HMR for now, the Webpack v3 template for Sapper works well, and getting the usual Webpack-related stuff up-and-running isn't too hard.

After following the install steps on the Sapper site, I wanted to install Tailwind and Purge. Svelte does this neat thing where it strips unused CSS out of components, but that really messes with build times as it tries to pull out every bit of Tailwind even on the dev server. I set up webpack.config.js like below and then imported a Tailwind-ified CSS file into client.js.

const webpack = require('webpack');
const config = require('sapper/config/webpack.js');
const pkg = require('./package.json');

const mode = process.env.NODE_ENV;
const dev = mode === 'development';

const extensions = ['.mjs', '.js', '.json', '.svelte', '.html'];
const mainFields = ['svelte', 'module', 'browser', 'main'];

module.exports = {
	client: {
		entry: config.client.entry(),
		output: config.client.output(),
		resolve: { extensions, mainFields },
		module: {
			rules: [
        {
        test: /\.css$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { importLoaders: 1 } },
          'postcss-loader'
        ]
        },
				{
					test: /\.(svelte|html)$/,
					use: {
						loader: 'svelte-loader',
						options: {
							dev,
							hydratable: true,
              hotReload: false
						}
					}
				}
			]
		},
		mode,
		plugins: [
			dev && new webpack.HotModuleReplacementPlugin(),
			new webpack.DefinePlugin({
				'process.browser': true,
				'process.env.NODE_ENV': JSON.stringify(mode)
			}),
		].filter(Boolean),
		devtool: dev && 'inline-source-map'
	},

	server: {
		entry: config.server.entry(),
		output: config.server.output(),
		target: 'node',
		resolve: { extensions, mainFields },
		externals: Object.keys(pkg.dependencies).concat('encoding'),
		module: {
			rules: [
        {
        test: /\.css$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { importLoaders: 1 } },
          'postcss-loader'
        ]
        },
				{
					test: /\.(svelte|html)$/,
					use: {
						loader: 'svelte-loader',
						options: {
							css: false,
							generate: 'ssr',
							dev
						}
					}
				}
			]
		},
		mode: process.env.NODE_ENV,
		performance: {
			hints: false // it doesn't matter if server.js is large
		}
	},

	serviceworker: {
		entry: config.serviceworker.entry(),
		output: config.serviceworker.output(),
		mode: process.env.NODE_ENV
	}
};

Then I set up my postcss.config.js like this:

const purgecss = require('@fullhuman/postcss-purgecss')({
  content: [
    './src/**/*.svelte',
  ],
  defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
})

module.exports = {
  plugins: [
    require('tailwindcss'),
     ...process.env.NODE_ENV === 'production'
      ? [purgecss]
      : []
  ]
}

After that, I had a pretty good setup—except for hot reloading (for now anyway). Oh, and Sapper doesn't set NODE_ENV to production, so you'll want to set that as part of your build and export npm scripts in package.json.

Brandon Pittman

Hello, there! I'm Brandon. I'm originally from Ohio, but now I live in Nagoya, Japan. I love the Web, programming, coffee, cooking and A Song of Ice and Fire.

My usual toolset is Vue.js, Gridsome and Tailwind. I'm interested in building web sites and applications using the JAMstack.