Snowpack

Star

Snowpack

Build web applications with less tooling and 10x faster iteration.

$ npx snowpack

Introduction

How It Works

  1. Instead of bundling on every change, just run Snowpack once right after npm install.
  2. Snowpack re-installs your dependencies as single JS files to a new web_modules/ directory. It never touches your source code.
  3. Build your app, import those dependencies via an ESM import, and then run it all in the browser.
  4. Skip the bundle step and see your changes reflected in the browser immediately after hitting save.
  5. Keep using your favorite web frameworks and build tools! Babel & TypeScript supported.

how it works illustration

// In a Snowpack application, this runs directly in the browser!
import React from '/web_modules/react.js';

// In a Snowpack application /w Babel:
import React from 'react';

Why?

Who’s Using Snowpack?

Who Should Use Snowpack?

Who Should Avoid Snowpack?

Modern Browser Support

Data on support for the es6-module feature across the major browsers from caniuse.com

By default, Snowpack installs npm dependencies as ES Module (ESM), which run natively wherever ESM syntax is supported. This includes ~90% of all browsers in use today. All modern browsers (Firefox, Chrome, Edge, Safari) have supported ESM since early 2018.

Additionally, Snowpack runs all dependencies through Babel via @preset/env to transpile any less-supported language features found in your dependencies. You can customize this behavior by setting your own “browserslist” key in your package.json manifest (see below).

Legacy Browser Support

legacy browser icons

As of v1.1, Snowpack also supports legacy browsers via the the --nomodule flag! This is a production-only flag that generates a legacy bundle of your entire application for older browsers that don’t understand ESM syntax. Because your application is already built to run in the browser, there’s no configuration or extra plugins required to use it.

If you need to support legacy browsers like IE11, UC Browser for Android (popular in China and India), Samsung Mobile, and Opera Mini then be sure to enable this feature.

Note that this introduces a production build step into your deployment process, if you don’t already have one. This is in addition to Snowpack’s “run-only-once-at-install-time” normal behavior, but still much lighter than Webpack/Parcel’s “run-on-every-change-at-dev-time” normal behavior.

Read our guide on Supporting Legacy Browsers for more details.

Load Performance

You can think of Snowpack like an optimized code-splitting strategy for your dependencies. Dependencies are bundled into individual files at install-time, with all internal package files bundled together as efficiently as possible. Any common, shared dependencies are moved into common, shared chunks in your web_modules/ directory.

But make no mistake: unbundled applications have a different performance story than bundled applications. Cache efficiency improves significantly (especially useful if you deploy multiple times per day) and the risk of shipping duplicate code across bundles goes to zero.

Unlike in traditional bundled applications, long (7+) chains of imports can slow down your first page load. How you weigh these pros and cons of unbundled production applications depends on your specific application and use-case.

Of course, you’re always free to add a bundler as a part of your production build pipeline only and you’ll continue to get the developer experience boost. Or, just use Snowpack to get started and then add a bundler whenever you feel you need to for performance. That’s what we did when we started www.pika.dev, and years later performance is still good.

To learn more about unbundled load performance, check out Max Jung’s post on “The Right Way to Bundle Your Assets for Faster Sites over HTTP/2”. It’s the best study on HTTP/2 performance & bundling we could find online, backed up by real data. Snowpack’s installation most closely matches the study’s moderate, “50 file” bundling strategy. Jung’s post found that for HTTP/2, “differences among concatenation levels below 1000 [small files] (50, 6 or 1) were negligible.”

Cache Performance

Snowpack performs best when it comes to caching. Snowpack keeps your dependencies separate from your application code and from each other, which gives you a super-optimized caching strategy by default. This lets the browser cache dependencies as efficiently as possible, and only fetch updates when individual dependencies are updated.

The same applies to unbundled application code as well. When you make changes to a file, the browser only needs to re-fetch that one file. This is especially useful if you manage multiple deployments a day, since only changed files need to be loaded.

Get Started

Installing Snowpack

npm install --save-dev snowpack
(or) yarn add --dev snowpack

# Then, run Snowpack:
npx snowpack

Installing a Dev Server

Snowpack is agnostic to how you serve your site during development. If you have a static dev server that you already like, use it to serve your Snowpack app. Otherwise, we recommend one of the following for local development:

Quick Start

0. First, create a new project directory

mkdir snowpack-demo
cd snowpack-demo
npm init --yes

1. Install your dependencies

npm install --save preact htm

We’ll start this tutorial by using Preact (similar to React) & HTM (similar to JSX). Even if you only want to use React and JSX, you should still start here. By the end of this tutorial, we’ll show how you can optionally replace Preact with React, and HTM with JSX (via Babel).

2. Run Snowpack to create your web_modules/ directory

npx snowpack
✔ snowpack installed: preact, htm. [0.50s]

If all went well, you should see a web_modules/ directory containing the files preact.js & htm.js. This is the magic of Snowpack: any npm package can be transformed from node_modules/PACKAGE_NAME to web_modules/PACKAGE_NAME.js, a bundled, single-file JS package that runs directly in the browser with no additional tooling needed!

Optionally, you can now run npm install snowpack --save-dev to speed up future Snowpack runs & run Snowpack inside of your package.json scripts. Otherwise, npx tends to re-install the tool before every run.

3. Create a simple HTML file for your application:

<!-- File Location: index.html -->
<!DOCTYPE html>
<html lang="en">
<head><title>Snowpack - Simple Example</title></head>
<body>
<div id="app"></div>
<script type="module" src="/src/app.js"></script>
</body>
</html>

4. Create a simple JavaScript application:

/* File Location: src/app.js */
// Import your web-ready dependencies
import { h, render } from '/web_modules/preact.js';
import htm from '/web_modules/htm.js';
const html = htm.bind(h);
// Create your main app component
function SomePreactComponent(props) {
return html`<h1 style="color: red">Hello, World!</h1>`;
}
// Inject your application into the an element with the id `app`.
render(html`<${SomePreactComponent} />`, document.getElementById('app'));

5. Serve & run your application

npx servor --reload

Start up a simple dev server (we recommend servor with the --reload flag for live-reload). Open your web browser and see your application running directly in the browser, instantly!

Any changes that you make to your src/app.js file are immediately reflected via either live-reload (if supported by your dev server) or a manual browser refresh. No bundlers, no build steps, and no waiting around for things to re-build after you make a change.

Open up your browser’s Dev Tools and debug your application directly in the browser. Browse your source code, set breakpoints, and get more useful error messages.

6. Optional Next Steps

Examples

Check out our repo’s README for a full list of example applications built with Snowpack: https://github.com/pikapkg/snowpack.

Bootstrap a Starter App

🆕 Check out snowpack-init! Instantly bootstrap a starter app with Snowpack. Choose between templates for Preact, Lit-HTML, TypeScript, and more.

Basic Usage

Snowpack has a single goal: to install web-ready npm packages to web_modules/ directory. It doesn’t touch your source code. What you build with it, which frameworks you use, and how you serve your project locally is entirely up to you. You can use as many or as few tools on top of Snowpack as you’d like.

Still stuck? See our Quick Start guide above for help to get started.

Zero-Config Installs (Default)

$ npx snowpack

By default, Snowpack will attempt to install all “dependencies” listed in your package.json manifest. If the package defines an ESM “module” entrypoint, then that package is installed into your new web_modules/ directory.

As long as all of your web dependencies are listed as package.json “dependencies” (with all other dependencies listed under “devDependencies”) this zero-config behavior should work well for your project.

Automatic Installs (Recommended)

$ npx snowpack --include "src/**/*.js"

With some additional config, Snowpack is also able to automatically detect dependencies by scanning your application for import statements. This is the recommended way to use Snowpack, since it is both faster and more accurate than trying to install every dependency.

To enable automatic import scanning, use the --include CLI flag to tell Snowpack which files to scan for. Snowpack will automatically scan every file for imports with web_modules in the import path. It will then parse those to find every dependency required by your project.

Remember to re-run Snowpack every time you import an new dependency.

Whitelisting Dependencies

  /* package.json */
"snowpack": {
"webDependencies": [
"htm",
"preact",
"preact/hooks", // A package within a package
"unistore/full/preact.es.js", // An ESM file within a package (supports globs)
"bulma/css/bulma.css" // A non-JS static asset (supports globs)
],
}

Optionally, you can also whitelist any dependencies by defining them in your “webDependencies” config (see below). You can use this to control exactly what is installed, including non-JS assets or deeper package resources.

Note that this config will disable the zero-config mode that attempts to install every package found in your package.json “dependencies”. Either use this together with the --include flag, or just make sure that you whitelist everything that you want installed.

Configuration

Snowpack’s behavior can be configured by CLI flags, a custom Snowpack config file, or both. See the table below for the full list of supported options.

CLI Flags

$ npx snowpack --optimize --clean

CLI flags will always be merged with (and take priority over) a config file.

Config Files

Snowpack supports configuration files in multiple formats. Snowpack will look for configuration in the current working directory in this order:

  1. package.json: A namespaced config object ("snowpack": {...}).
  2. snowpack.config.js: A JS file exporting a config object (module.exports = {...}).
  3. snowpack.config.json: A JSON file containing config ({...}).

Configuration Options

{
"webDependencies": [
"htm",
"preact",
"preact/hooks", // A package within a package
"unistore/full/preact.es.js", // An ESM file within a package (supports globs)
"bulma/css/bulma.css" // A non-JS static asset (supports globs)
],
"installOptions": {
"dest": "web_modules",
"clean": false,
"optimize": false,
"babel": false,
"include": "src/**/*.{js,jsx,ts,tsx}",
"exclude": ["**/__tests__/*", "**/*.@(spec\|test).@(js\|mjs)"],
"strict": false,
"sourceMap": true,
"remotePackage": [],
"remoteUrl": "https://cdn.pika.dev",
"nomodule": "src/index.js",
"nomoduleOutput": "app.nomodule.js"
},
"dedupe": ["lit-element", "lit-html"],
"rollup": {
"plugins": []
}
}
Config Option Type Description
webDependencies string[] (Recommended) Set exactly which packages to install with Snowpack.
installOptions.* object (Optional) Configure how packages are installed. See table below for all options.
namedExports object (Optional) If needed, you can explicitly define named exports for any dependency. You should only use this if you’re getting "'X' is not exported by Y" errors without it. See rollup-plugin-commonjs for more documentation.
dedupe string[] (Optional) If needed, deduplicate multiple versions/copies of a packages to a single one. This helps prevent issues with some packages when multiple versions are installed from your node_modules tree. See rollup-plugin-node-resolve for more documentation.
rollup.plugins object[] (Optional) Specify Custom Rollup plugins if you are dealing with non-standard files.

Install Options (installOptions.*)

CLI Flag Config Option Type Default Description
--dest dest string web_modules Configure the install directory.
--clean clean boolean false Delete the existing dest directory (any any outdated files) before installing.
--optimize optimize boolean false Recommended for production: transpile, minify, and optimize installed dependencies (this may slow down snowpack!).
--babel babel boolean false Transpile installed dependencies. Enabled automatically by --optimize. Can be disabled via CLI flag via --no-babel.
--include include string Scans source files to auto-detect install targets. Supports glob pattern matching. See our Automatic Installs guide for more info.
--exclude exclude string See Description. Exclude files from --include scanning. Supports glob pattern matching. Defaults to exclude common test file locations: ['**/__tests__/*', '**/*.@(spec|test).@(js|mjs)']
--strict strict boolean false Only install pure ESM dependency trees. Fail if a CJS module is encountered.
--stat stat boolean false Logs install statistics after installing, with information on install targets and file sizes. Useful for CI, performance review.
--hash hash boolean false Add a ?rev=XXX hash to each import in the import map / used by Babel plugin. May cause double-requests, if one top-level package imports another.
--source-map sourceMap boolean See Description. Emit source maps. Enabled automatically by --optimize. Can be disabled via CLI flag via --no-source-map.
--nomodule nomodule string Enable a <script nomodule> bundle. Value should be the entrypoint of your application to start bundling from. See our Supporting Legacy Browsers guide for more info.
--nomodule-output nomoduleOutput string app.nomodule.js File name/path for the nomodule output.
--external-package externalPackage string[] [] (Advanced use only) Mark these packages as external to be left unbundled and referenced remotely. Example: --external-package foo will leave in all imports of foo.

You can also use the --help flag to see a list of these options on the command line.

Advanced Usage

Importing Packages by Name

// ✔ RUNS IN THE BROWSER
import React from '/web_modules/react.js';

// ✘ WON'T RUN IN THE BROWSER
// ✔ CAN BE TRANSFORMED AUTOMATICALLY BY BABEL, ETC.
import React from 'react';

Browsers can only import by URL, so importing a package by name requires additional tooling. Below are some options for build tools that handle the import name -> URL transformation for you automatically. This can be especially useful when you are migrating an existing app to Snowpack.

Babel Support: If you use Babel with Snowpack, you can use our Babel plugin to support package name imports. The plugin reads any packages name imports in your source code and rewrites them to full "/web_modules/${PACKAGE_NAME}.js" URLs that run in the browser. This way, you can keep using the package name imports that you’re used to without needing a full web app bundler. Check out our Babel guide below for instructions on setting up Babel.

/* .babelrc */
"plugins": [
["snowpack/assets/babel-plugin.js", {}],
]

TTypeScript Support: If you use TTypescript (note: this is a separate tool from TypeScript) you can use the @magic-works/ttypescript-browser-like-import-transformer plugin to support package name imports. The transformer reads import declarations in your source code and rewrites them to full "/web_modules/${PACKAGE_NAME}.js" URLs that run in the browser.

/* tsconfig.json */
{
"compilerOptions": {
"module": "es2015",
"plugins": [
{
"transform": "@magic-works/ttypescript-browser-like-import-transformer",
"after": true,
"bareModuleRewrite": "snowpack"
}
]
}
}

Import Maps

Warning: Import Maps are an experimental web technology that is not supported in every browser. For polyfilling import maps, check out es-module-shims.

// Include this in your application HTML...
<script type="importmap" src="/web_modules/import-map.json"></script>
// ... to enable browser-native package name imports.
import * as _ from 'lodash';

Snowpack generates an Import Map with every installation. If your browser supports Import Maps, you can load the import map somewhere in your application and unlock the ability to import packages by name natively in the browser (no Babel step required).

Production Optimization

Tree-shaking example

$ npx snowpack --optimize

By default, Snowpack installs dependencies unminified and optimized for development. When you’re ready for production, run Snowpack with the --optimize flag to enable certain production-only optimizations:

Supporting Legacy Browsers

Nomodule example

If you are building a Single Page Application (SPA), run Snowpack with --nomodule and pass in your application entrypoint. Then, create a second script tag in your application to point legacy browsers towards this legacy build alongside your modern type="module" entrypoint:

<!-- Ignored by legacy browsers: -->
<script type="module" src="/src/index.js"></script>
<!-- Ignored by modern browsers: -->
<script nomodule src="/web_modules/app.nomodule.js"></script>

After doing this, run Snowpack to generate a /web_modules/app.nomodule.js application bundle that will automatically be run on older, legacy browsers.

Similar to the --optimize flag, --nomodule must be run before every deployment. When using this flag, we strongly recommend scripting your deployments to guarantee that Snowpack is never skipped before a deployment (ex: npm run deploy or npm run build:production).

This bundle step was designed to be simple and general-purpose enough to cover any browser-ready application with zero config. But, if you do get to the point where you’d like to manually configure/control your production bundles, we’d recommend setting up a bundler yourself separately from Snowpack.

Caching in the Browser

The unbundled applications that Snowpack allows you to build can be ultra cache-efficient, with zero code duplication across page loads. But proper caching requires some helpful information from the server. Below is a list of caching strategies for your server that you can use with Snowpack.

ETag Headers

ETag support is the easiest caching strategy to implement, and many hosting providers (like Zeit) will enable this for you automatically. From MDN:

The ETag HTTP response header is an identifier for a specific version of a resource. It lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. Additionally, etags help prevent simultaneous updates of a resource from overwriting each other (“mid-air collisions”). – https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag

Service Workers

Service workers can implement a client-side cache for your site regardless of what your server responses look like. Check out this article on "Caching Files with Service Worker ", and be sure to read our Workbox guide below for help using Workbox with Snowpack.

Automatic Cache Busting via Import URL

The browser cache is keyed by unique resource URL. This means that different URL query params will result in different cache hits (and cache misses) even if the server ignores them. Applications built with Babel can leverage this behavior to automatically control the cache.

The Snowpack Babel plugin supports an "addVersion" option that will automatically add the package version of any package import as a query parameter of the import URL in the final build. This effectively creates a new cache entry every time a dependency changes, which allows your server to send more aggressive long-term cache headers for the web_modules/ directory.

/* .babelrc */
"plugins": [
["snowpack/assets/babel-plugin.js", {"addVersion": true}],
]
// src/ File Input
import Foo from 'package-name';
// lib/ Babel Output
import Foo from '/web_modules/package-name.js?v=1.2.3';

Customize Transpilation

  /* package.json */
"browserslist": " >0.75%, not ie 11, not UCAndroid >0, not OperaMini all",

Snowpack runs all dependencies through Babel (via @preset/env) to transpile unsupported language features in your dependencies. This is useful when packages rely on modern language features that your users’ browsers may not support.

By default, Snowpack will transpile using the recommended target string shown above. You you can customize this behavior by setting your own top-level “browserslist” key in your package.json manifest.

Run After Every Install

  /* package.json */
"scripts": {
"prepare": "snowpack"
}

You can optionally add “snowpack” as a "prepare" script to your package.json and npm/yarn will automatically run it after every new dependency install. This is recommended so that new dependencies are automatically included in your web_modules/ directory immediately.

Importing CSS

// ✘ NOT SUPPORTED OUTSIDE OF BUNDLERS
import './style.css';

No browser today supports importing a CSS file directly from JS. Instead, you’ll want to use one of the following libraries/solutions:

  1. Recommended! If you’re building a simple app, consider defining your CSS inside your HTML using a <style> block.
  2. Recommended! csz - “Runtime CSS modules with SASS like preprocessing”
  3. @emotion/core - “Simple styling in React.” (Requires Babel)
  4. Most CSS-in-JS libraries will work without a bundler, although some may require extra Babel plugins to work.

Importing Images

// ✘ NOT SUPPORTED OUTSIDE OF BUNDLERS
import './photo.png';

No browser today supports importing an image directly from JS. Instead, you’ll want to use one of the following libraries/solutions:

  1. Recommended! Keep referencing images by URL. You can put any image URL directly into an <img> src tag: <img src="/img/photo.png">.

Custom Rollup Plugins

You may encounter non-JS npm packages that can only run with additional parsing/processing. Svelte packages, for example, commonly include .svelte files that will require additional tooling to parse and install for the browser.

In these rare cases, you can add your own Rollup plugins to your Snowpack config to handle these special files:

/* snowpack.config.js */
const rollupPluginSvelte = require('rollup-plugin-svelte');

module.exports = {
rollup: {
plugins: [rollupPluginSvelte()]
}
};

Refer to Rollup’s documentation on plugins for more information.

Guides

Snowpack dramatically speeds up your development time by removing the need for a web application bundler. But you can still use build tools like Babel or TypeScript and get the same speed improvements without the bundler. On every change, your build tool will only need to update a single file, instead of entire bundles.

Below are a collection of guides for using different web frameworks and build tools with Snowpack. If you’d like to add your own, feel free to hit the pencil icon in the top-right to edit our docs.

Babel

To use Babel with Snowpack:

  1. Make sure that your entire application lives inside a source directory (ex: src/).
  2. Run Babel to build your src/ application to an output lib/ directory (ex: babel src/ --out-dir lib --watch)
  3. Update your HTML entrypoint to point to your lib/ directory.
  4. Now make changes to your src/ directory, and see them build instantly.
  5. Optional: Check out our Babel plugin for importing packages by name in the guide above.

TypeScript

TypeScript expects imports to be by package name, and won’t be able to understand your “web_modules/” imports by default. You’ll need to follow one of the following guides based on your setup:

With Babel (Recommended)

If you build your site with Babel, you can keep importing packages by name thanks to our Snowpack Plugin. TypeScript will be able to understand these “package name imports” and properly map them back to the type definitions found in your node_modules/ directory when it type checks your source directory.

To use TypeScript with Babel, set up Babel with the following plugins (feel free to add others):

/* .babelrc */
{
"presets": ["@babel/preset-typescript"],
"plugins": ["snowpack/assets/babel-plugin.js"]
}

Once you have Babel building your site correctly, TypeScript should be able to understand your source directory without any additional configuration.

Without Babel:

To use TypeScript with Snowpack, you’ll need to set up your tsconfig.json to understand “web_module” import paths. See our annotated tsconfig.json example below:

"compilerOptions": {
// Choose your target based on which browsers you'd like to support.
"target": "es2017",
// Required: Use module="esnext" so that TS won't compile/disallow any ESM syntax.
"module": "esnext",
// Optional, but recommended
"moduleResolution": "node",
// Optional, but recommended
"baseUrl": ".",
// Required: Map "/web_modules/*" imports back to their node_modules/ TS definition files.
"paths": {
"/web_modules/*.js": [
"node_modules/@types/*",
"node_modules/*",
"web_modules/*.js"
]
},
// ...
}

Vue

/*
* NOTE: The Vue package points to the runtime-only distribution by default.
* Unless you are using the Vue CLI, you'll most likely need the full browser build.
* Runtime only: `import Vue from "/web_modules/vue.js"`
*
* https://vuejs.org/v2/guide/installation.html#Explanation-of-Different-Builds
*/

import Vue from "/web_modules/vue/dist/vue.esm.browser.js";

// $ snowpack --include "src/index.js"
// ✔ snowpack installed: vue/dist/vue.esm.browser.js. [1.07s]

Psst… are you an expert on Vue? We’d love your help writing a short guide for authoring .vue SFC’s and then compiling them to valid JS!

Preact

// File: src/index.js
import { h, Component, render } from '/web_modules/preact.js';
// Optional, if using HTM instead of JSX and Babel:
import htm from '/web_modules/htm.js';
const html = htm.bind(h);

// $ snowpack --include "src/index.js"
// ✔ snowpack installed: preact, htm. [1.06s]

Relevant guides:

React

import React, { useState } from '/web_modules/react.js';

Important: React is not yet published with ES Module support, and the way that it’s written makes it impossible to install as a dependency (“Error: ‘__moduleExports’ is not exported by node_modules/react/index.js”). However, React is still supported thanks to our actively-maintained ESM builds of React & React-DOM. You can install them both in your project under an alias like so:

npm install react@npm:@pika/react react-dom@npm:@pika/react-dom
   yarn add react@npm:@pika/react react-dom@npm:@pika/react-dom

When installed under the react/react-dom alias, all tooling (including Snowpack) will benefit from these easier-to-analyze ESM distributions of the popular packages.

JSX

It’s important to keep in mind that JSX isn’t really JavaScript. JSX is a build-time syntax that only your build tooling understands, and that doesn’t run directly in any browser. In any app you work on (bundled or unbundled) you’ll need to use a build tool like Babel or TypeScript to transpile JSX into regular old JavaScript before shipping it to the browser.

To use JSX with Snowpack, you can either:

  1. Use TypeScript with “–jsx” mode enabled. See our “TypeScript” guide for more.
  2. Use Babel with a plugin like @babel/plugin-transform-react-jsx or a framework-specific preset like @babel/preset-react. See our “Babel” guide for more.
  3. Use a JSX-like alternative that can run in the browser. Jason Miller’s htm is a great option (keep reading).

HTM

HTM is “a JSX alternative using […] JSX-like syntax in plain JavaScript - no transpiler necessary.” It has first-class support for Preact (built by the same team) but also works with React.

https://www.pika.dev happily used HTM + Preact for many months before adding Babel and switching to React + TypeScript.

// File: src/index.js
import { h, Component, render } from '/web_modules/preact.js';
import htm from '/web_modules/htm.js';
const html = htm.bind(h);

return html`<div id="foo" foo=${40 + 2}>Hello!</div>`

// $ snowpack --include "src/index.js"
// ✔ snowpack installed: preact, htm. [1.06s]

lit-html

lit-html is “an efficient, expressive, extensible HTML templating library for JavaScript.” Similarly to HTM, lit-html uses tagged template literals for JSX-like syntax in the browser without requiring any transpilation.

// File: src/index.js
import { html } from "/web_modules/lit-html.js";
import { until } from "/web_modules/lit-html/directives/until.js";

// $ snowpack --include "src/index.js"
// ✔ snowpack installed: lit-html, lit-html/directives/until.js [0.17s]

Important: There should only ever be one version of lit-html run in the browser. If you are using third-party packages that also depend on lit-html, we strongly recommend adding "lit-html" to your dedupe config to prevent Snowpack from installing multiple versions:

// File: package.json
"snowpack": {
// ...
"dedupe": ["lit-html"]
},

Important: lit-html directives aren’t exported by the main package. Run Snowpack with the --include flag so that Snowpack can automatically detect these imports in your application. Otherwise, add them each separately to the webDependencies whitelist in your package.json:

// File: package.json
"snowpack": {
"webDependencies": [
"lit-html",
"lit-html/directives/until.js",
],
},

LitElement

LitElement is “a simple base class for creating fast, lightweight web components” built on top of lit-html. Read our lit-html guide for important information.

// File: src/index.js
import { LitElement, html, css } from "/web_modules/lit-element.js";
import { repeat } from "/web_modules/lit-html/directives/repeat.js";

// $ snowpack --include "src/index.js"
// ✔ snowpack installed: lit-html, lit-element [0.25s]

Svelte

Svelvet is a Svelte compiler + dev environment that integrates directly with Snowpack. Svelvet watches your source directory and compiles your Svelte components to standard, browser-ready JavaScript.

<!-- File: src/App.svelte -->
<!-- Taken from example app: https://github.com/jakedeichert/svelvet/tree/master/examples/basic -->
<script>
import Header from './components/Header';
import Footer from './components/Footer/index';
</script>
<Header />
<Footer />

Run npx svelvet on the command line to start Svelvet in dev mode. For more information on Svelvet and instructions on how to build for production, check out the full guide.

SASS

# 1. Build your CSS (add --watch to watch for changes)
sass ./scss:./css
# 2. Load the output css anywhere in your application
<link rel="stylesheet" type="text/css" href="/css/index.css">

SASS is a “mature, stable, and powerful professional grade CSS extension language” that compiles to CSS. Use the SASS CLI to compile your SASS into CSS, and then load that CSS anywhere in your application.

You might also like: csz, run-time CSS modules with support for SASS-like selectors. CSZ runs directly in the browser, so you can skip the SASS build/watch step.

Tailwind CSS

# 1. Build your CSS
npx tailwind build styles.css -o css/tailwind.css
# 2. Load the output file anywhere in your application
<link rel="stylesheet" type="text/css" href="/css/tailwind.css">

Tailwind works as-expected with Snowpack. Just follow the official Tailwind Install Guide. When you get to step #4 (“Process your CSS with Tailwind”) choose the official Tailwind CLI option to generate your CSS. Import that generated CSS file in your HTML application.

Styled Components

// File: src/index.js
import React from "/web_modules/react.js";
import {render} from "/web_modules/react-dom.js";
import styled from "/web_modules/styled-components.js";

// $ snowpack --include "src/index.js"
// ✔ snowpack installed: react, react-dom, styled-components. [1.06s]

Relevant guides:

Material UI

// File: src/index.js
import React from "/web_modules/react.js";
import {render} from "/web_modules/react-dom.js";
import Button from "/web_modules/@material-ui/core/Button/index.js";

// $ snowpack --include "src/index.js"
// ✔ snowpack installed: @material-ui/core/Button/index.js, react, react-dom. [1.64s]

Relevant guides:

Workbox

The Workbox CLI integrates well with Snowpack. Run the wizard to bootstrap your first configuration file, and then run workbox generateSW to generate your service worker.

Remember that Workbox expects to be run every time you deploy, as a part of a production “build” process (similar to how Snowpack’s --optimize flag works). If you don’t have one yet, create a "deploy" and/or "build" script in your package.json to automate your production build process.

Migrating an Existing App

How you migrate an existing app to Snowpack depends on which Bundler features/plugins you’re using. If you’re only using the import statement to import other JavaScript files, the process should only take a couple of minutes. If you’re importing CSS, images, or other non-JS content in your application, you’ll need to first get rid of those Webpack-specific imports before migrating away from Webpack.

Assuming you’ve removed all code specific to your bundler, you can use the following rough plan to migrate to Snowpack.

  1. Use Babel to assist in the migration. If you don’t want to use Babel, don’t worry; You can always remove it after migrating.
  2. Follow the Babel guide above to build your existing src/ directory to a new lib/ directory.
  3. Follow the Babel Plugin guide above to add the Snowpack Babel plugin so that your package imports will continue to run as is. Check your output lib/ directory to make sure that dependency imports are being rewritten as expected.
  4. Run your application in the browser! If everything is working, you’re done! Otherwise, use your browser’s dev tools to hunt down any remaining issues in your application code.

Migrating to Webpack/Parcel

Snowpack is designed for zero lock-in. If you ever feel the need to add a traditional application bundler to your stack (for whatever reason!) you can do so in seconds.

Any application built with Snowpack should Just Work™️ when passed through Webpack/Rollup/Parcel. If you are importing packages by full URL (ex: import React from '/web_modules/react.js'), then a simple Find & Replace should help you re-write them to the plain package names (ex: import React from 'react') that bundlers expect.