background by
@geographyofrobots

Snowpack

The O(1) build tool for modern web apps.

Get Early Access Take me to the v1.x docs

Who’s Using Snowpack?

Overview

What is Snowpack?

Snowpack is a web app build tool that works without bundling. Thanks to ESM imports Snowpack is able to remove the expensive (and unnecesary) bundling step from your dev workflow. That means no startup time spent waiting for your application to bundle PLUS no time wasted rebundling on every change.

The result is a developer experience that starts up fast, stays fast, and still bundles/optimizes your production builds.

Key Features

Library Support

  • React
  • Preact
  • Svelte
  • Vue
  • lit-html
  • lit-element
  • Styled Components
  • Tailwind CSS
  • and more!

Tooling Support

  • Babel
  • TypeScript
  • PostCSS
  • esbuild
  • 11ty
  • and more!

Browser Support

Snowpack builds your site for both modern and legacy browsers (even IE11). You can control and customize this behavior with the “browserlist” package.json property.

The only requirement is that during development you use a modern browser. Any recent release of Firefox, Chrome, or Edge will do. This is required to support the modern, bundle-free ESM imports that load your application in the browser.

Get Started

Install Snowpack

Snowpack v2.0 is in early access! We’ll be launching the official 2.0.0 release later this month, but we’d love for you to try it out today. Just don’t forget to install @next when you install with npm/yarn:

# using npm
npm install --save-dev snowpack@next

# using yarn
yarn add --dev snowpack@next

Snowpack can also be installed globally via npm install -g snowpack. But, we recommend installing locally in every project via --save-dev/--dev. You can run the Snowpack CLI locally via package.json “scripts”, npm’s npx snowpack, or via yarn snowpack.

Create Snowpack App (CSA)

The easiest way to get started with Snowpack is via Create Snowpack App (CSA). CSA automatically initializes a starter application for you with an already-configured, Snowpack-powered dev environment.

If you’ve ever used Create React App, this is a lot like that!

npx create-snowpack-app new-dir --template [SELECT FROM BELOW] [--use-yarn]

Official App Templates

Migrating an Existing App

Migrating an existing app to Snowpack is generally easy, since Snowpack supports almost every build tools that you’re already using today (Babel, PostCSS, etc). If this is your first time using Snowpack you should start with a Create Snowpack App (CSA) template, copy over your “src” & “public” files from your old app, and then run snowpack dev to finish up the migration and troubleshoot any remaining issues.

CSA is a good starting point for existing applications because it has a few common tools (like Babel) built in by default to replicate the full feature set of a traditional bundled app. CSA is also meant to be a drop-in replacement for Create React App, so any existing Create React App project should run with zero changes needed.

If you run into issues, check out our Advanced Usage section for guides on importing CSS, asset references, and more.

Commands

snowpack dev

Snowpack’s dev server is an instant dev environment for any web application. snowpack dev stays fast by skipping all unecceary bundling during development and serving individual files directly to the browser. That means zero upfront startup cost: Snowpack only starts building your app when you make your first request. This scales especially well to large projects, where you’d otherwise commonly see 30+ second dev startup times with a traditional bundler.

This magic is all possible thanks to Snowpack’s npm package installer, which installs your packages so that they can run directly in the browser. When you develop or build your application, Snowpack automatically rewrites your imports to point to your Snowpack-installed, ready-to-run web dependencies.

// Your Code:
import * as React from 'react';
import * as ReactDOM from 'react-dom';

// Build Output:
import * as React from '/web_modules/react.js';
import * as ReactDOM from '/web_modules/react-dom.js';

Snowpack supports JSX & TypeScript source code by default, compiling your files to JavaScript before sending them to the browser. Connect any other favorite tools to fully customize and extend your build pipeline. your build. Build Scripts & Plugins tell Snowpack how to transform your source files, allowing you to code in whatever language you’d like. Vue, Svelte, PostCSS, SASS… go nuts!

snowpack build

When you’re ready to deploy your application, run snowpack build to generate a static production build of your site. Building is tightly integrated with your dev setup so that you are guaranteed to get a working copy of the same code you saw during development.

The default output of the snowpack build command is an exact copy of your unbundled dev site. Deploying unbundled code is fine for simple sites, but you may want to optimize your site even further by bundling your final deployment for production.

Snowpack supports production bundling via a simple, zero-config --bundle flag. snowpack build --bundle runs your final build through Parcel, a popular web application bundler. By bundling together your JavaScript and CSS files into larger shared chunks, you may see a production speed up as your users have fewer files to download.

snowpack install

Snowpack originally became famous for it’s npm package install. Since then, the installer has been integrated directly into the dev & build workflows so that you no longer need to run the Snowpack installer yourself. Feel free to skip this section and come back later: you probably won’t ever need to run this command.

But, if you want to run the installer yourself, snowpack install will install your dependencies into a new web_modules/ directory. Snowpack will scan your project for ESM import statements to find every npm package used by your application. You can also provide a list of package names manually via the “knownEntrypoints” config.

# Example: Snowpack detects `import 'react'` & `import 'react-dom'` statements in your "src/" code.
✔ snowpack install complete. [0.88s]

⦿ web_modules/ size gzip brotli
├─ react-dom.js 128.93 KB 39.89 KB 34.93 KB
└─ react.js 0.54 KB 0.32 KB 0.28 KB
⦿ web_modules/common/ (Shared)
└─ index-8961bd84.js 10.83 KB 3.96 KB 3.51 KB

From here, any web_modules/ package can be imported and run directly in the browser with zero additional bundling or tooling required. This ability to import npm packages natively in the browser (without a bundler) is the foundation that all no-bundle development (and the rest of Snowpack) is built on top of.

<!-- This runs directly in the browser! -->
<script type='module'>
import * as React from '/web_modules/react.js';
console.log(React);
</script>

Again, all of this is built into Snowpack dev & build commands by default. But, you can use the install command to install & manage web-ready npm packages for your own dev server or build pipeline.

Features

Hot Module Replacement

Hot Module Replacement (HMR) is the ability to update your web app during development without refreshing the page. Imagine changing some CSS, hitting save, and then instantly seeing your change reflected on the page without a refresh. That’s HMR.

Snowpack supports full HMR out-of-the-box for the following served files:

Popular frameworks can also be set up for HMR. Create Snowpack App (CSA) ships with HMR enabled by default for all of the following frameworks. If you’re not using CSA, you can setup HMR in your own application with a simple plugin or a few lines of code:

For more advanced, bare-metal HMR integrations, Snowpack created ESM-HMR, a standard HMR API for any ESM-based dev environment. Any HMR integration built for ESM-HMR will run on Snowpack and any other ESM-HMR-enabled dev server. To use the HMR API directly (via import.meta.hot) check out the ESM-HMR spec to learn more.


if (import.meta.hot) {
import.meta.hot.accept(({module}) => {
// Accept the module, apply it to your application.
});
import.meta.hot.dispose(() => {
// Cleanup any side-effects. Optional.
});
}

Import CSS

Snowpack supports basic CSS imports inside of your JavaScript files. While this isn’t natively supported by any browser today, Snowpack’s dev server and build pipeline both handle this for you.

// Loads './style.css' onto the page
import './style.css'

Snowpack also supports any popular CSS-in-JS library. If you prefer to avoid these non-standard CSS imports, check out csz. CSZ is a run-time CSS module library with support for SASS-like syntax/selectors.

Import CSS Modules

Snowpack supports CSS Modules for CSS files using the [name].module.css naming convention. CSS Modules allow you to scope your CSS to unique class names & identifiers. CSS Modules return a default export (styles in the example below) that maps the original identifier to it’s new, scoped value.

/* src/style.module.css */
.error {
background-color: red;
}
// 1. Converts './style.module.css' classnames to unique, scoped values.
// 2. Returns an object mapping the original classnames to their final, scoped value.
import styles from './style.module.css'

// This example uses JSX, but you can use CSS Modules with any framework.
return <div className={styles.error}>Your Error Message</div>;

Import JSON

Snowpack supports importing JSON via ESM import. While this isn’t yet supported in most browsers, it’s a huge convenience over having vs. use fetch() directly.

// JSON is returned as parsed via the default export
import json from './data.json'

Import Images & Other Assets

import img from './image.png'; // img === '/src/image.png'
import svg from './image.svg'; // svg === '/src/image.svg'

// This example uses JSX, but you can use these references with any framework.
<img src={img} />;

All other assets not explicitly mentioned above can be imported to get a URL reference to the asset. This can be useful for referencing assets inside of your JS, like creating an image element with a src attribute pointing to that image.

Proxy Requests

Snowpack’s dev server can proxy requests during development to match your production host environment. If you expect a certain API to be available on the same host as your web application, you can create a proxy via a proxy Build Script:

// snowpack.config.json
// Example: Proxy "/api/pokemon/ditto" -> "https://pokeapi.co/api/v2/pokemon/ditto"
{
"scripts": {
"proxy:api": "proxy https://pokeapi.co/api/v2 --to /api"
}
}

Learn more about Build Script integrations.

JSX

Compile to JavaScript

When you write your web app with JSX, Snowpack will automatically build all .jsx & .tsx files to JavaScript during development and production builds. This works for both React & Preact as long as the file includes an import of React or Preact.

Note: JSX must live in .jsx files. JSX in .js files is not supported.

If needed, you can optionally define your own JSX->JavaScript build step via a Build Script integration.

// snowpack.config.json
// Optional: Build JSX files with Babel (must define your own babel.config.json)
{
"scripts": {
"build:jsx": "babel --filename $FILE",
}
}

TypeScript

Compile to JavaScript

Write your web app with TypeScript, and Snowpack will automatically build all .ts & .tsx files to JavaScript. Snowpack will not perform any type checking by default (see below), only building from TS->JS.

If needed, you can optionally define your ownTS->JS build step via a Build Script integration.

// snowpack.config.json
// Optional: Build TS & TSX files with Babel (must define your own babel.config.json)
{
"scripts": {
"build:ts,tsx": "babel --filename $FILE",
}
}

Type Checking During Development

You can integrate TypeScript type checking with Snowpack via a Build Script integration. Just add the TypeScript compiler (tsc) as a build command that gets run during your build with a --watch mode for development.

// snowpack.config.json
// Example: Connect TypeScript CLI (tsc) reporting to Snowpack
{
"scripts": {
"run:tsc": "tsc --noEmit",
"run:tsc::watch": "$1 --watch"
}
}

Import Maps

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

Snowpack generates an Import Map with every installation to web_modules/import-map.json. 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).

<!-- 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';

Note that Snowpack already performs these rewrites for you at both dev and build time, so this is only useful for experimentation and 3rd-party tooling integrations. As a general rule: if you don’t care about this file, keep it but feel free to ignore it.

Legacy Browser Support

You can customize the set of browsers you’d like to support via the package.json “browserslist” property. When running snowpack build, Snowpack will have Parcel transpile all JavaScript files according to the browser targets you’ve defined.

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

Note: During development (snowpack dev) we perform no transpilation for older browsers. Make sure that you’re using a modern browser during development.

Installing Non-JS Packages

When installing packages from npm, You may encounter some non-JS code 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.

Because our internal installer is powered by Rollup, you can add Rollup plugins to your Snowpack config to handle these special, rare files:

/* snowpack.config.js */
module.exports = {
rollup: {
plugins: [require('rollup-plugin-svelte')()]
}
};

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

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.

Config Files

Snowpack supports configuration files in multiple formats, sorted by priority order:

  1. --config [path]: If provided.
  2. package.json: A namespaced config object ("snowpack": {...}).
  3. snowpack.config.js: (module.exports = {...}).
  4. snowpack.config.json: ({...}).

CLI Flags

$ snowpack --help

CLI flags will be merged with (and take priority over) your config file values. Every config value outlined below can also be passed as a CLI flag. Additionally, Snowpack also supports the following flags:

All Config Options

{
"knownEntrypoints": [
"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)
],
"homepage": "/your-project",
"installOptions": { /* ... */ },
"devOptions": { /* ..... */ }
}

Top-Level Options

Install Options

Dev Options

Build Scripts

Snowpack is more than just a static file server, it’s a platform to power your entire build pipeline. Babel, TypeScript, PostCSS, and any favorite build tool can be connected directly into Snowpack via simple, 1-line transformations. These transformations are called build scripts.

Overview

A build script is just a simple bash (CLI) command. Snowpack will pipe your source files into matching script commands (via stdin) and then send it’s output (via stdout) to the browser.

If you’ve ever worked with package.json “scripts”, creating your own build scripts should hopefully feel familiar:

// snowpack.config.json
// 1. Pipe every .css file through PostCSS CLI
// 2. Pipe every .js & .jsx file through Babel CLI
{
"scripts": {
"build:css": "postcss",
"build:js,jsx": "babel --filename $FILE"
}
}

The "build" script type is the basic building block of any Snowpack build pipeline. In this example babel & postcss are both used to process your code at dev time and then again when building for production. Each file is piped through the proper CLI to get the final build output.

<!-- Example: Load "src/index.jsx" in the browser -->
<script type="module" src="/src/index.js"></script>

By default, build scripts are run against every matching file in your project. For large/complex projects, we recommend that you organize your source code into subdirectories (src/, public/, etc) that you can whitelist via “mount:” scripts.

All Script Types

Snowpack supports several other script types in addition to the basic "build" type. These different script types serve different goals so that you can fully customize and control your dev environment:

Script Variables

Snowpack provides a few variables that you can use to make your build scripts (and plugins) more dynamic. Snowpack will replace these with the correct value when run:

Script Modifiers ("::")

You can customize your build scripts even further via the "::" script modifier token. These act as addons to a previous matching script that extend that script’s behavior:

// snowpack.config.json
{
"scripts": {
// During build, runs TypeScript to lint your project.
"run:ts,tsx": "tsc --noEmit",
// During dev, runs `tsc --noEmit --watch` for live feedback.
"run:ts,tsx::watch": "$1 --watch",
}
}

Note that $1 can be used with a script modifier to reference the original script. See the section on Script Variables above.

Build Plugins

For more powerful integrations, Snowpack supports custom build plugins. A build plugin is more than just a bash script: it’s loaded via Node.js to customize and extend your Snowpack dev environment & build process.

Overview

A build plugin offers one of several different hooks into your application:

Check out our advanced plugin guide for more details and instructions for how to write your own.

Plugin API

👉 Check out our advanced guide and learn how to create your own plugin.

Connect a Plugin

Connect a build plugin to Snowpack via the "plugins" array in your Snowpack config;

// snowpack.config.json
{
"plugins": ["@snowpack/plugin-babel"]
}

This is all you need to connect the plugin. If a build script is provided by the plugin, it will be automatically added to your “scripts” config. You can customize that script (and which files it will match) by defining the build script yourself.

// snowpack.config.json
// Optional: Define your own build script for "@snowpack/plugin-babel".
{
"plugins": ["@snowpack/plugin-babel"],
"scripts": {"build:js,jsx,mjs,cjs": "@snowpack/plugin-babel"}
}

Plugin vs Script?

You can get pretty far with build scripts alone. If you just want to convert your source code to JavaScript/CSS and you have a CLI that can make that transformation for you, then a build script is probably fine.

But, there are a few reasons you may want to use a build plugin instead of a normal build script:

Speed: Some CLIs may have a slower start-up time, which may become a problem as your site grows. Plugins can be faster across many files since they only need to be loaded & initialized once and not once for every file.

"scripts": {
// Speed: The Babel plugin is ~10x faster than using the Babel CLI directly
"build:js,jsx": "@snowpack/plugin-babel",
}

Lack of CLI: Some frameworks, like Svelte, don’t maintain dedicated CLIs. Snowpack Plugins allow you to tap into a tool’s JS interface directly without building a whole new CLI interface.

"scripts": {
// Lack of CLI: There is no Svelte CLI. Our plugin taps directly into the Svelte compiler
"build:svelte": "@snowpack/plugin-svelte",
}

Custom Control: You can write your own project-specific plugins easily, and load them via relative path without ever needing to publish them.

"scripts": {
// Custom Behavior: Feel free to build your own!
"build:vue": "./my-custom-vue-plugin.js",
}

Recipes

Below are a collection of guides for using different web frameworks and build tools with Snowpack. If you’d like to contribute a new recipe, feel free to edit the docs to add your own.

Supported Libraries

All of the following frameworks have been tested and guaranteed to work in Snowpack without issues. If you encounter an issue using any of the following, please file an issue.

Some libraries use compile-to-JS file formats and do require a special build script or plugin. See the guidea below for examples.

Babel

Babel will automatically read plugins & presets from your local project babel.config.* config file, if one exists.

via plugin (Recommended)

// snowpack.config.json
"plugins": ["@snowpack/plugin-babel"],
"scripts": {
"build:js,jsx": "@snowpack/plugin-babel"
}

via @babel/cli

// snowpack.config.json
// NOTE: Not recommended, Babel CLI is slower than the plugin on large sites.
"scripts": {
"build:js,jsx": "babel --filename $FILE"
}

Vue

// snowpack.config.json
// Note: The plugin will add a default build script automatically
"plugins": ["@snowpack/plugin-vue"]

Svelte

// snowpack.config.json
// Note: The plugin will add a default build script automatically
"plugins": ["@snowpack/plugin-svelte"]

PostCSS

// snowpack.config.json
"scripts": {
"build:css": "postcss"
}

Tailwind CSS

Tailwind ships with first-class support for PostCSS. Copy the PostCSS script above, and then grab the recommended PostCSS plugin from the official Tailwind CSS Docs.

SASS

Make sure that you have Sass installed, and then add a “build:scss” script to convert your Sass files to CSS.

// snowpack.config.json
"scripts": {
"build:scss": "sass"
}

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 package.json "deploy" and/or "build" scripts to automate your production build process.

Leaving Snowpack

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 already importing packages by name in your source code (ex: import React from 'react') then you should be able to migrate to any popular bundler without issue.

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 name imports that most bundlers expect.