DEV Community

Cover image for Replacing Webpack with Snowpack in a Phoenix Application
Richard Taylor
Richard Taylor

Posted on • Originally published at richardtaylor.dev

Replacing Webpack with Snowpack in a Phoenix Application

Snowpack 3 was recently released and now comes with esbuild support built-in which should mean much faster builds with fewer dependencies, so I replaced Webpack in a fresh phoenix app to see the difference.

tldr; It is incredibly fast and lightweight. See this commit for all the changes required to get this working.

I created a new Phoenix application with LiveView to test this out. LiveView so all the asset config is already set up and we can switch out Webpack for Snowpack.

Firstly I removed all node_modules, webpack.config.js, yarn.lock, .babelrc and all dependencies in package.json except phoenix_html and nprogress which we'll keep.

Then I ran yarn add --dev snowpack to add Snowpack, the final package.json looks like this.

// /assets/package.json

{
  "repository": {},
  "description": " ",
  "license": "MIT",
  "scripts": {
    "deploy": "snowpack build",
    "watch": "snowpack build --no-bundle --watch"
  },
  "dependencies": {
    "phoenix_html": "file:../deps/phoenix_html"
  },
  "devDependencies": {
    "nprogress": "^0.2.0",
    "snowpack": "^3.0.11"
  }
}
Enter fullscreen mode Exit fullscreen mode

I also added the equivalent deploy and watch commands to replace the Webpack ones.

Next was to create a config file for Snowpack that matches the default asset file structure for Phoenix, and configures esbuild to optimize the assets for production.

// /assets/snowpack.config.js

module.exports = {
  mount: {
    "js": { url: "/js" },
    "css": { url: "/css" },
    "static": { url: "/", static: true, resolve: false }
  },
  buildOptions: {
    out: "../priv/static/"
  },
  optimize: {
    entrypoints: ["./static/index.html"],
    bundle: true,
    minify: true,
    target: 'es2018'
  },
};
Enter fullscreen mode Exit fullscreen mode

Only odd thing here is the fake HTML entrypoint I had to set up which references the app.js and app.css. I found without this the optimizer didn't run for app.css, maybe there is a better way of doing this.

Snowpack creates a _snowpack folder in the output directory so we need to add this to the Static Plug in /lib/snowball_web/endpoint.ex.

Snowpack creates ES modules for your JS assets so we need to update the script tag for app.js from type="text/javascipt" to type="module".

Then we need to update the asset watcher that Phoenix starts up to run Snowpack, which now looks like this.

# /config/dev.exs

watchers: [
  node: [
    "node_modules/snowpack/index.bin.js",
    "build",
    "--watch",
    cd: Path.expand("../assets", __DIR__)
  ]
]
Enter fullscreen mode Exit fullscreen mode

The final hurdle was getting the phoenix and phoenix_live_view assets to load. Snowpack didn't like the default import that Phoenix generates so I ended up linking to the Snowpack skypack CDN to get ES module versions of those dependencies and that works great.

The result is incredibly fast start and rebuild times for assets in development and in production.

# development

$ snowpack build --no-bundle
[snowpack] ! building source files...
[snowpack] ✔ build complete [0.01s]
[snowpack] ! building dependencies...
[snowpack] ✔ dependencies ready! [0.09s]
[snowpack] ! verifying build...
[snowpack] ✔ verification complete [0.00s]
[snowpack] ! writing build to disk...
[snowpack] ! optimizing build...
[snowpack] ✔ optimize complete [0.09s]
[snowpack] ▶ Build Complete!

✨  Done in 0.68s.
Enter fullscreen mode Exit fullscreen mode
# production (on Heroku)

$ snowpack build
[snowpack] ! building source files...
[snowpack] ✔ build complete [0.03s]
[snowpack] ! building dependencies...
[snowpack] ✔ dependencies ready! [0.29s]
[snowpack] ! verifying build...
[snowpack] ✔ verification complete [0.00s]
[snowpack] ! writing build to disk...
[snowpack] ! optimizing build...
[snowpack] ✔ optimize complete [0.05s]
[snowpack] ▶ Build Complete!

Done in 0.84s.
Enter fullscreen mode Exit fullscreen mode

That is a vast improvement over the equivalent Webpack setup and results in 17MB of node_modules vs 225MB before the change.

There are caveats to using the built-in esbuild as it says it isn't quite ready for production use yet, but you can add other tried-and-tested plugins to Snowpack if you prefer.

Overall the future is looking good, and I'll be using this from now on.

If you have any comments please add them to this thread on Twitter.

Discussion (0)