How JavaScript works: understanding Snowpack, the next generation JavaScript bundler

Lawrence Eagles
SessionStack Blog
Published in
9 min readMay 31, 2022

--

Post # 64 of the series How Javascript Works is dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building SessionStack, a JavaScript tool for developers to identify, visualize, and reproduce web app bugs through pixel-perfect session replay.

Introduction

According to the documentation, Snowpack is a lightning-fast frontend build tool, designed for the modern web. It is an alternative to heavier, more complex bundlers like Webpack or Parcel in your development workflow.

Traditional bundle-based bundlers like Webpack and Rollup bundles all our application files and assets before serving them via the dev-server. And this process is repeated when we update and save the file. The result of this is slow dev-server startup time that decreases linearly as our application size increases.

Snowpack takes a different approach. And by leveraging native JavaScript modules — ESM, Snowpack eliminates unnecessary work and stays fast regardless of the application size. Bundlers that utilize ESM are called ESM-bundlers and another good alternative is Vite which we have covered in a previous article in this series.

Also, Snowpack implements a novel concept known as unbundled development. An idea that involves serving our application unbundled during development. And according to the official documentation, Snowpack’s unbundled web development server starts up in 50ms or less and stays fast in large projects. We will learn more about Snowpack’s unbundled development in the next section.

Snowpack is extensible via plugins and it supports Hot module refresh — HMR out of the box. Unlike Webpack, Snowpack simplifies development by providing built-in support for JSX, TypeScript, React, Preact, CSS Modules, and more. And this removes the need of installing and configuring loaders.

In the next section, we will get started with Snowpack by learning about the files it supports in detail.

Getting Started With Snowpack

In this section, we will take a deeper look at Snowpack and learn about some of its key features.

Supported Files

Out of the box, Snowpack supports a lot of file types:

  • JavaScript — .js and .mjs files
    Snowpack leverages the native JavaScript module system — ESM.
    Since ESM is supported by most browsers, Snowpack can import code directly to the browser during development. And this is what makes Snowpack’s unbundled development workflow possible.
  • TypeScript — .ts and .tsx files
    Although snowpack does not support type-checking out of the box, it provides built-in support to build TypeScript files into JavaScript. However, type-checking can be added via the @snowpack/plugin-typescript plugin.
  • JSX — .jsx and .tsx files
    Snowpack provides built-in support to build JSX files — .jsx and .tsx to JavaScript. But it does not support JSX in .js and .ts files by default. To add this feature, you can build your JavaScript using the @snowpack/plugin-babel plugin. This plugin gives full compiler customization via Babel.
  • Also, if you are using Preact, Snowpack will automatically detect this and switch to using the Preact-style JSX h() function.
  • CSS — .css files
    Snowpack allows you to import CSS files directly into your application. But it provides support for compile-to-CSS languages like Sass & Less via plugins.

CSS Modules — .module.css
Snowpack supports CSS modules using the [name].module.css naming convention.
CSS Modules export a special styles object by default. And this object maps your original classnames to unique identifiers. And importing a CSS module will automatically apply that CSS to the page.

  • Images & Assets — .svg, .jpg, .png, etc.
    Snowpack enables importing all assets via ESM import. And this returns a URL reference to the final built asset, enabling us to reference non-JS assets by URL.
    An example is creating an image element with an src attribute pointing to that image.
  • WebAssembly — .wasm
    Snowpack supports loading WebAssembly — WASM files directly into your application via the browser’s WebAssembly API. And you can learn more about this from the WebAssembly guide.
  • JSON — .json
    Snowpacks support importing JSON files directly into your application. And in default import the full JSON object is returned.

Using these file types does not require a loader or custom configuration. And we can extend Snowpack to support more languages by implementing the methods specified in the tooling guide.

Unbundled Development

Popular bundle-based build tools like Webpack and Rollup build applications using the bundled development pattern. The bundled development pattern involves bundling all the application files and assets before they are served. And on every save, the entire application is re-bundled even if only a single file was updated.

Running the whole application through a bundler on every save adds additional work and makes the dev workflow unnecessarily complex.

Snowpack uses a novel idea called unbundled development. The unbundled development pattern involves serving the unbundled — individual application files to the browser. These files can still be built using tools like Babel, TypeScript, and SASS and then loaded with their dependencies into the browser using ESM import and export. And when a file changes, Snowpack rebuilds only that file.

This single file builds on every update — in the unbundled development is faster, predictable, easy to configure & debug, and deterministic.

Also, in the unbundled development pattern, every file is built individually and cached indefinitely. So your dev environment will only build a file once, and your browser will never download a file twice — until it changes. This is the real power of unbundled development.

The image below compares the bundled development pattern with the unbundled development pattern:

It is important to note that the unbundled development pattern supports traditional bundlers for building your application for production. So when you bundle for production, you can plug in your favorite bundler via an official Snowpack plugin e.g the Snowpack plugin for Webpack.

Consequently, Snowpack gives you the best of both worlds: fast, unbundled development with optimized performance in your bundled production builds.

When compared to the bundled development pattern, the unbundled development pattern has the following advantages:

  • Single-file builds are fast.
  • Single-file builds are deterministic.
  • Single-file builds are easier to debug.
  • Project size doesn’t affect dev speed.
  • Individual files cache better.

The DevServer

Snowpack dev-server only builds files requested by the browser. And the result of this is that Snowpack can startup instantly. In contrast, there is always a time lag of about 30+ sec with the dev-servers of traditional bundlers.

Also, with the unbundled development pattern, Snowpack can scale to infinitely large projects without slowing down.

The Build pipeline

The build your application for production, Snowpack uses the command snowpack build. And running this command tells Snowpack to generate a static production build of your site.

Note one of Snowpack’s design principles is: You should be able to use a bundler because you want to, and not because you need to.

So Snowpack treats bundling as optional production optimization, that you can skip over until you need it.

By default running the snowpack build command builds your application using the unbundled approach. However, this lacks some performance optimization. And you can get this by leveraging your favorite traditional bundler via plugins like the @snowpack/plugin-webpack and snowpack-plugin-rollup-bundle.

Integrating these bundles is a seamless process. And it requires no configuration because Snowpack builds your application before sending it to the bundler. So the bundler’s only job is to build HTML, CSS, and JS.

Snowpack.config.js File

This is a config object that holds the configuration used to customize the behavior of Snowpack.

To generate this file run the command: snowpack init as seen below:

The snowpack.config.js file supports many options, and we will learn about some of them as we build our application in a subsequent section. But you can get more details about these options here.

Setting Up a Snowpack Project

The easiest way to bootstrap a Snowpack project is to use the Create-Snowpack App — CSA CLI tool.

The Creat Snowpack App command has the following format:

The command above allows you to specify an app-folder-name, an app-template-NAME, a package manager such as yarn, and more.

And Snowpack provides both official and community templates for different frameworks and libraries.

Bootstrap Application and Install Dependencies

To bootstrap a Snowpack application, run the following command:

And start the dev server with the following command:

cd react-snowpack
npm run start

And we get:

Now, we install React and React Dom by running:

npm install react react-dom — save

Customize project structure

Since we are adding a bunch of files let’s structure the application properly. In the root directory, create an src and a public directory.

Now move the index.css file and index.html file to the public directory. Then rename the index.js file to index.jsx and move it into the src directory. You can do all these via the CLI by running the following commands:

Structuring your like this Snowpack since the files are in different places. But you can solve this by customizing the snowpack.config.js file. Relace the mount option in the snowpack.config.js file with the following code:

Note the mount configuration changes where Snowpack looks for and builds files.

Since Snowpack builds files in src directory, like src/index.js into /dist/index.js, you’ll need to change that path in your index.html. We also, you’ll need to add a div with id=”root” for React-Dom. So update the body element in the index.html file as seen below:

Creating Components

Now we need to create our application components. In the src directory create an App.jsx component containing the following code:

Also, create a folder called components in the src directory. And in the components folder create a Counter.jsx component and a Counter.module.css file.

Add the following code to the Counter.jsx component:

And add the following styles to the Counter.module.css file:

Updating Render

Now replace the code in the index.jsx with the following code:

Finally, build your application by running:

npm run build

And start the dev-server by running:

npm start

And we get:

Customizing Snowpack With Plugins

We can enhance and customize Snowpack via plugins. And in this section, we will use the Snowpack SASS plugin to add support for SASS. To do this, install the plugin by running:

npm i @snowpack/plugin-sass

Now, update the plugin property of the snowpack.config.js file as seen below:

plugins: [‘@snowpack/plugin-sass’],

Then rename the Counter.module.css to Counter.module.scss and also update the import as seen below:

import styles from "./Counter.module.scss"

Build your application and start the dev server. And we get:

So everything works fine.

Conclusion

Snowpack is a next-generation JavaScript bundler that simplifies and speeds up development and the build process. And with the novel and revolutionary unbundled development principle Snowpack is one of the pioneers of ESM-bundlers. And many of the awesome features of Vite are inspired by Snowpack.

In this article, we have learned a lot about Snowpack. And I hope you will give it a try in your next front-end project.

ESM bundlers like Snowpack make it much easier for software to have efficient and high-performing code. And since we all like to apply new technologies & upgrade our code so even if we feel we’ve tested everything before release, it’s always necessary to verify that our users have a great experience with our product.

A solution like SessionStack allows us to replay customer journeys as videos, showing us how our customers actually experience our product. We can quickly determine whether our product is performing according to their expectations or not. In case we see that something is wrong, we can explore all of the technical details from the user’s browser such as the network, debug information, and everything about their environment so that we can easily understand the problem and resolve it. We can co-browse with users, segment them based on their behavior, analyze user journeys, and unlock new growth opportunities for our applications.

There is a free trial if you’d like to give SessionStack a try.

SessionStack replaying a session

Interested in more about JavaScript? Check out all “How JavaScript works” publications here.

--

--