Learn Snowpack: A High-Performance Frontend Build Tool

Learn Snowpack

In this article, we’ll take a first look at Snowpack — specifically Snowpack 3, which at the time of writing has just been released. Snowpack is a front-end build tool that’s been getting a lot of attention in the community for offering a different approach from tools like webpack, and I’ve been keen to check it out for a while. Let’s dive in!

A History of Build Tools

Before we look into Snowpack, we need to take a quick moment to understand how and why bundlers like webpack came to be. JavaScript’s lack of a module system prior to ES2015’s modules meant that, in the browser, the closest we could get to modules was to split our code up into files that put code into the global scope, as this was how we shared it between files. It was common to see code like this:

window.APP = {} window.APP.Authentication = {...} window.APP.ApiLoader = {...} 

When Node.js arrived and gained popularity, it had a module system in the form of CommonJS:

const Authentication = require('./Authentication.js') const APILoader = require('./APILoader.js') 

Once this became popular as part of Node, people wanted to be able to use it in the browser. That’s when tools started emerging that did this; they could take an application that used CommonJS modules, and bundle it into one large JavaScript file, with all the requires removed, that could be executed in the browser. Browserify was the first such tool that I can remember using to do this, and, to be honest, it felt like magic! This was around the time that webpack came to be, and other tools also supported using CommonJS.

When ES Modules were first introduced (see “Understanding ES6 Modules” for a refresher), people were keen to use them, but there were two problems:

  1. Whilst the spec was done, browsers didn’t support ES Modules.
  2. Even if a browser did support ES Modules, you probably still wanted to bundle in production, because it takes time to load in all the modules if they’re defined as separate files.

Webpack (and others) updated to support ES Modules, but they would always bundle your code into one file, both for developing and for production. This meant that a typical workflow is:

  1. Edit a file in your application.
  2. Webpack looks at which file changed, and rebundles your application.
  3. You can refresh the browser and see your change. Often, this is done for you by a webpack plugin such as hot module reloading.

The problem here lies in step two as your application grows in size. The work for webpack to spot a file change and then figure out which parts of your application to rebundle into the main bundle can take time, and on large applications that can cause a serious slowdown. That’s where Snowpack comes in …

Snowpack’s Approach

Snowpack’s key selling point for me is this line from their documentation:

Snowpack serves your application unbundled during development. Each file needs to be built only once and then is cached forever. When a file changes, Snowpack rebuilds that single file.

Snowpack takes full advantage of ES Modules being supported across all major browsers and doesn’t bundle your application in development, but instead serves up each module as a single file, letting the browser import your application via ES Modules. See “Using ES Modules in the Browser today” for more detail on browsers and their support for unbundled ES Modules.

It’s important to note at this point that you must use ES Modules to use Snowpack. You can’t use CommonJS in your application.

This however raises a question: what if you install a dependency from npm that does use CommonJS? Although I hope one day that the majority of npm packages are shipped as ES Modules, we’re still a fair way off that, and the reality is even if you build an application exclusively in ES Modules, it’s highly likely at some point you’ll need a dependency that’s authored in CommonJS.

Luckily, Snowpack can deal with that too! When it sees a dependency (let’s say, React), in your node_modules folder, it can bundle just that dependency into its own mini-bundle, which can then be imported using ES Modules.

Hopefully you can see why Snowpack caught my eye. Let’s get it up and running and see how it feels to use on an application.

Getting Started

To start with, I create a new empty project folder and run npm init -y to get me up and running. This creates a basic package.json which I can go in and edit later if I want to. You can also run npm init without the -y, which will make npm prompt you to answer questions to fill in the details in your package.json. I like using -y to quickly get up and running; I can edit the package.json later.

I then install Snowpack as a developer dependency:

npm install --save-dev snowpack 

And now I add two scripts into my package.json:

"scripts": { "start": "snowpack dev", "build": "snowpack build" }, 

This sets us up two npm run commands:

  • npm run start will run Snowpack in development mode.
  • npm run build will run a production build of Snowpack, which we’ll talk more about later.

When we run our application, Snowpack fires up a little development server that will run our application locally. It will look for an index.html file, so let’s create one of those and also create app.js, which for now will just log hello world to the console:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Snowpack testing</title> </head> <body> <script src="./app.js"></script> </body> </html> 
console.log('hello world') 

Now we can run npm run start (or npm start for short — start is one of the npm lifecycle methods where you don’t need to prefix it with run).

You should see your terminal output look something like this:

snowpack http://localhost:8080 • http://172.18.33.234:8080 Server started in 80ms. ▼ Console [snowpack] Hint: run "snowpack init" to create a project config file. Using defaults... [snowpack] Nothing to install. 

The first part of the output tells us that Snowpack is running on localhost:8080. The next line prompts us to create a Snowpack configuration file, which we’ll do shortly, but it’s the last line that I want to highlight:

[snowpack] Nothing to install. 

This is Snowpack telling us that it’s checked for any npm modules that need dealing with, and it hasn’t found any. In a moment, we’ll add an npm package and take a look at how Snowpack deals with it.

Generating a Configuration File

You can run npx snowpack init to generate the configuration file as the command line output suggests. We won’t be needing to change Snowpack’s behavior until we come to bundling for production, but if you do you can create this file and configure a wide range of options to get Snowpack running just how you want it to.

Writing in ES Modules

Let’s create another JavaScript file to see how Snowpack deals with multiple files. I created api.js, which exports a function that takes a username and fetches some of their public repositories from GitHub:

export function fetchRepositories(user) { return fetch(`https://api.github.com/users/${user}/repos`) .then(response=> response.json()); } 

Then, in app.js, we can import and use this function. Feel free to replace my GitHub username with your own!

import {fetchRepositories} from './api.js'; fetchRepositories('jackfranklin').then(data => console.log(data)); 

Save this file, and run Snowpack again if you didn’t leave it running previously. In the browser console, you’ll see an error:

Uncaught SyntaxError: Cannot use import statement outside a module 

This is because of our <script> tag in our HTML file:

<script src="./app.js"></script> 

Because ES Modules behave slightly differently from code that doesn’t use ES Modules, it’s not possible for browsers to just start supporting ES Modules in all scripts. Doing so would almost certainly break some existing websites, and one of the main goals of JavaScript is that any new features are backwards compatible. Otherwise, every new JS feature might break thousands of existing websites!

In order to use ES Modules, all we need to do is tell the browser that by giving the script tag a type of module:

<script type="module" src="./app.js"></script> 

And when you save that, your browser should refresh automatically (another nice thing Snowpack does out of the box) and you’ll see a list of GitHub repositories logged to the console.

Continue reading Learn Snowpack: A High-Performance Frontend Build Tool on SitePoint.