Configuration & learning curve
It’s no secret that webpack can be hard to configure, especially for beginners. But it’s also important to remember that webpack is capable of really big, powerful stuff, so it makes sense that some configuration and knowledge is required to use all that power. Still, even today, there’s no simple command for a “simple” webpack setup, and it still requires a steep learning curve and understanding of what it’s doing for most people.
By comparison, Snowpack doesn’t need any configuration for most JS apps with its include flag. You simply tell it which of your files to scan for imports like this:
npx snowpack --include './src/**.js'
That’s it! Assuming your JS files are all valid ESNext (i.e. no JSX or special syntax), it will install all of your dependencies in a way you can ship your code straight to production. But on the topic of ES Modules, many might not realize that the imports most of us write don’t closely follow the ES Modules spec:
-import React from 'react'; // 🚫 invalid ESM +import React from '/web_modules/react.js'; // ✅ valid ESM
Although most of us write
react , not
react.js, browsers can’t understand it without the file extension because it’s not a valid URL¹. Snowpack allows you to bundle npm packages as single files, so it can generate that
react.js that browsers can understand. While this may be a change in your codebase, fortunately, it can be as easy as a few find-and-replace commands (for example, replacing
When it comes to alternate syntaxes, too, like
.svelte, Snowpack has some starter projects that help get up-and-running there, too. Depending on the setup, this could mean either the same level of configuration as webpack, or a little less! But rarely does using Snowpack result in more to manage.
Advantage: 🗻 Snowpack
¹ If you’re thinking “wait… can an ESM import be at any URL? Even a remote one?” then you’re onto something big! ESM can use remote URLs, and we’ll get back to that point in the Using a CDN section below.
Writing ES Modules directly for the browser means no wait and instant preview of your code. Whereas webpack runs on every save, Snowpack only runs whenever you install a new npm package (which gets rarer over time).
What’s worse: with bundling, your time waiting on your code to build scales linearly with your app size. In other words, the larger the app, the longer you wait. But with Snowpack no matter how big your application code gets, no build time means no build time. While saving milliseconds may not seem like a lot at first glance, over the course of hundreds of times a day, over a few years, that can become hours of your life!
Advantage: 🗻 Snowpack
webpack’s caching story revolves around how you split code, or don’t. Changing a single line of code or a single dependency will cause users to re-download most, or all, of that weight. Your code-splitting setup can minimize that, but in most cases webpack results in repeat users re-downloading more code than what you changed. ²
But there’s an even bigger danger lying in wait with webpack— duplicate code! When you start to split your bundles, if you’re not carefully analyzing all contents using webpack-bundle-analyzer, you’ve likely duplicated code. So if a user visits your app, they might not only be downloading a lot of JS — they might be downloading it multiple times!
Snowpack + ES Modules follow a different strategy. You bundle nothing, instead opting to expose the ESM import tree to the browser. Because the browser understands your app structure, it can then efficiently fetch what it needs directly, and only what it needs. In letting the browser do all this work for you, it required zero time commitment from you and it’s impossible to duplicate code or dependencies.
As a result, when using Snowpack + ESM, if you changed your code or an npm dependency a user visiting your site only needs to download the exact files & dependencies that changed and nothing more. If
react’s version changed, the user downloads the new version of
react and everything else it pulled from cache. When it comes to caching, Snowpack really shines—and in this regard it’s nearly perfect³.
Advantage: 🗻 Snowpack
² webpack’s core proficiency is bundling, so by design it will try and combine any code and dependencies it can. It’s difficult to configure webpack where one changed file results in only that file being re-downloaded by the user.
³ I say “nearly-perfect” because tree-shaking your app code isn’t yet possible with ESM (covered in the Tree Shaking section).
If you read through the Caching section in this post, a question that may have popped up is how do webpack and Snowpack compare in terms of network performance? Loading network assets is one of the most complicated, interesting, and nuanced comparisons between the two tools.
Snowpack’s author, Fred K. Schott, touches on this subject in greater detail in A Future Without Webpack. But to give a crude simplification, Snowpack + ESM exposes the import tree to the browser. That means the browser downloads and scans a
<script> tag, then lists the imports in that file. It then scans those imports, and those imports’ imports, until it reaches the very end of the “tree.” That process of crawling through files will generally result in more network requests than webpack’s bundling. But more files to crawl doesn’t necessarily mean slower.
While reducing your number of requests seems like an open-and-shut case for bundling, and, yes, bundling files will almost always improve download speed for each individual file, the reality is that the situation of modern JS app has a few more factors to consider than the sum total of network requests, such as:
- The more async
import()statements you use in your app, the more both Snowpack & webpack will perform identically because the JS fetching is determined by your app, not the tooling
- Also taking into account the Using CDNs section, the more your app takes advantage of CDNs, the more Snowpack & webpack perform similarly
- If using a service worker that aggressively fetches assets, then Snowpack & webpack will also behave similarly as loading behavior is dictated by that
So let’s recap, reducing requests is where webpack shines, but only if your app consists of synchronous
import statements to other modules hosted on your server. The more lazy-loaded code you serve, and the more modules you serve from a remote CDN, the more Snowpack & webpack perform similarly both in number of requests and overall network speed.
Advantage: 📦 webpack but only on initial cacheless load
Arguably the coolest feature of ESM is the ability to use
import statements with remote modules:
import preact from 'https://cdn.pika.dev/preact'; // ┳┻| // ┻┳|_ Psst… // ┳┻|•.•) this is a really // ┳┻|⊂ﾉ big deal // ┻┳|
This is huge! Even though tens of thousands of sites use React, most of them probably hosted their own version rather than using a CDN, which meant as you’ve crawled the web you’ve probably downloaded the same version of React hundreds of times and counting. It’s not the websites’ faults, though—loading
<script> tags for npm dependencies in 2020 is so ancient! It undoes all of the great work that ES Modules have given us.
But with ESM, you actually can load modules from remote servers—whether that’s a CDN like Pika or even a common CDN you’ve set up yourself.
So Snowpack is the clear winner here, right? Not so fast! While, yes, Snowpack does support remote modules out-of-the-box, a third-party webpack plugin has come about that lets you import external packages. And it’s apparently on its way to become part of the webpack 5 release! As of the writing of this article, webpack 5 isn’t out yet, so it’s unclear what importing remote URLs in webpack looks like. But I’m going to count it as possible for webpack (at least through webpack-external-import before the v5 release).
Advantage: 👯♂️ both!
Tree-shaking, in case you’re unfamiliar with the term, is the process of removing unused code from your app. Say you installed and imported the heavyweight
lodash in your app, but ended up not using it. That shouldn’t count against you, right? With tree-shaking, your app is as small as it can be.
webpack is practically the poster child of tree-shaking. It is a tree-shaking beast and it does it all for you without you having to configure a thing.
But just because Snowpack is an install-time tool doesn’t mean it can’t tree-shake! When you run
snowpack--optimize, it can tree-shake the dependencies you use. Granted, Snowpack can’t tree-shake your app code, but that’s also because it doesn’t touch your app code. If you need tree-shaking for your app code, you will have to use a separate tool with Snowpack. But a fair assessment is for each tool, both tree-shake all the code they manage.
Advantage: 👯♂️ both!
The last comparison that’s worth making is flexibility. When it comes to Snowpack vs webpack, how many different setups, and types of apps can each handle? How much control does each give you over unique setups?
- Snowpack requires ESM-ready packages, which means you can only use a small subset of npm packages
- Though it’s been said in this article multiple times already, Snowpack by design won’t touch your app code; that’s for you to configure
Snowpack is meant to help give you training wheels to writing ESM-ready code for browsers without giving up npm packages and the JS ecosystem. Beyond that, it’s up to you!
If you need to handle code transformation, transpilation, minification, etc. then webpack has just about all the tools you need. webpack isn’t the only tool—you can use Babel by itself, or Rollup, or any individual thing you need, but when it comes to the Swiss-Army-Knife-complete-package, webpack is unrivaled. Even though flexibility is a vague and ambiguous term, whatever you need, there’s probably a webpack plugin for that™.
Advantage: 📦 webpack
Perhaps you’ve landed on this article wondering ‘Should I start my project with webpack or Snowpack?’ I’d recommend Snowpack, but only if you can use ESM-ready packages (which you can search for on pika.dev). It’s quick and painless and you can always easily migrate to webpack if needed. But based on your needs, you may find one of the comparisons above at the heart of your project that may tip you in either direction.
Let me know in the comments how this comparison stacked up, or if there are any other comparisons you wanted to see!
Plug: LogRocket, a DVR for web apps
LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.