We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Out of the box, the Phoenix asset build system bundles custom JS alongside default Phoenix JS dependencies, using esbuild, into a script that the app serves with all its pages.
I was braced for this to be much more complicated than it is.
Basic usage is described in the comments at the top of assets/js/app.js
. To paraphrase, with a little colour:
-
The convention is to put source files for front-end assets into your app’s
assets
directory. The suggested location for third-party JS files isassets/vendor
, and for LiveView hooks it’sassets/js/hooks
. -
In
assets/js/app.js
:-
import
the new JS files—or one file thatimports
its own dependencies. - Add any calls to imported JS functions that should be executed when the browser loads a page.
-
When the project’s assets are built, esbuild bundles the contents of assets/js/app.js
and its import
ed dependencies (and their dependencies, recursively) into priv/static/assets/app.js
where it’s available to be served as a static asset.
Assets get built either by a watcher on the running dev server or by a Mix task. When the server starts in dev mode, it, in turn, starts an esbuild process in watch mode that builds and rebuilds the JS bundle as its source files change.
The assets.build
and assets.deploy
Mix aliases include an esbuild
Mix task to build JS assets (assets.deploy
minifies them as well).
The bundled priv/static/assets/app.js
is served at /assets/app.js
and sourced in <script>
tags in the app’s root layout, so that it runs when the browser loads a page.
Building blocks
Chris McCord’s gist on upgrading from Phoenix v1.5 to v1.6 illuminates the moving parts of the esbuild-based system that come with a freshly generated Phoenix app.
The esbuild Hex package
An Elixir wrapper for esbuild that provides the Esbuild module and Mix tasks to install and run esbuild.
config.exs
Contains a configuration entry for the esbuild wrapper, with a named execution profile that gives a set of args and options common to all out-of-the-box esbuild invocations in the app.
This profile sets assets/js/app.js
as the entry point and priv/static/assets
as the output directory to pass to esbuild.
mix.exs
Includes the esbuild
Hex package in the project dependencies; it’s only part of the runtime in a development environment.
Defines Mix aliases assets.build
and assets.deploy
that run the esbuild
Mix task with the execution profile from config.exs
.
dev.exs
Contains the dev-environment-only configuration for the app’s Endpoint
module. Configures a “watcher” that calls the Esbuild.install_and_run
function to start esbuild, in watch mode, again with the profile defined in config.exs
.
root.html.heex
The app’s root layout. Sources the bundled JS assets with <script>
tags.
router.ex
Following this even further: the app’s Router module includes the browser
pipeline, where the put_root_layout
plug incorporates root.html.heex
, and thus the app’s bundled app.js
, into every page that gets piped through the browser
pipeline.
Or
Nothing stops you from adding your own <script>
tags, in the app’s root layout or somewhere else, pointing to some script that a user’s browser can reach: a location on the net, or a file your app serves with a Static
plug.
Taking that further, you could tweak the out-of-the-box Phoenix asset management system to generate another custom bundle and reference that script from only certain layouts or pages.
References
- Asset management: Third-party JS packages (Phoenix docs)
- The assets directory (Phoenix docs)
- Chris McCord’s gist on upgrading to Phoenix 1.6 (shows how to manually configure an app for esbuild instead of webpack)
- Elixir wrapper for esbuild (GitHub repo)
- Aliases (Mix docs)
- Phoenix.Endpoint module runtime configuration (with watcher config) (Phoenix docs)
- Config API (Elixir docs)
- Pipelines and plugs (Phoenix docs)