PhoenixAssetPipeline
Asset pipeline for Phoenix and Phoenix LiveView. It builds and caches application assets, minifies CSS/HTML classes, generates image and SVG variants, compresses static files, and serves everything from one manifest.
Requirements
- Elixir 1.18+
- Erlang/OTP 28+
- Phoenix LiveView
- Rust 2024
- Bun packages declared in the application's
assets/package.json
Installation
def deps do
[{:phoenix_asset_pipeline, "~> 2.0"}]
end
Prepare module-scope classes before Elixir and build the manifest after the application compiler:
def project do
[
compilers:
[:phoenix_live_view, :phoenix_asset_pipeline_prepare] ++
Mix.compilers() ++
[:phoenix_asset_pipeline]
]
end
Configure the endpoint and HEEx engine:
manifest_mode =
case config_env() do
:dev -> :cached
:test -> :cached
:prod -> :precompiled
end
config :phoenix, template_engines: [heex: PhoenixAssetPipeline.HTML.Engine]
config :phoenix_asset_pipeline,
bun_version: "1.3.14",
endpoint: MyAppWeb.Endpoint,
manifest_mode: manifest_mode,
otp_app: :my_app
Start the pipeline before the endpoint:
children = [
PhoenixAssetPipeline,
MyAppWeb.Endpoint
]
HTML
Use the macros in the application's HTML surface:
def html do
quote do
use PhoenixAssetPipeline.HTML.Macros
import PhoenixAssetPipeline.Components
import PhoenixAssetPipeline.Helpers
end
end
Render manifest-backed assets:
<html data-d={asset_digest()}>
<head>
{script("app", async: true, crossorigin: true)}
{style("app")}
</head>
<body>{@inner_content}</body>
</html>
Serve static files before the router:
plug PhoenixAssetPipeline.Plug, :put_private_phoenix_assigns
plug PhoenixAssetPipeline.Plug.Static, only: MyAppWeb.static_paths()
plug MyAppWeb.Router
Classes
Calls from functions and HEEx templates resolve through the current manifest at runtime. Module attributes and component defaults embed stable minified literals prepared before Elixir compilation; production builds allocate them deterministically.
@container {:div, class: class("h-full")}
def button(assigns) do
~H"""
<button class={class(["button", {"enabled", @enabled}])}>...</button>
"""
end
The prepare and final compilers share the same mapping, so module values, runtime values, manifest entries, and CSS selectors remain consistent without a second Elixir compilation.
Assets
Default inputs:
assets/js/*.{js,ts,jsx,tsx,mjs,cjs}assets/css/*.cssassets/img/**/*.{png,webp,avif}assets/svg/**/*.svgassets/svg/sprites/<name>/*.svg- Phoenix LiveView colocated assets
priv/static/**
Bun installs application-side dependencies when the package or lockfile changes. Production builds require
assets/bun.lock and install with --frozen-lockfile. Image variants use vix/libvips. Brotli, gzip, deflate, and
Zstandard representations are stored only when they are smaller than the original.
Common options:
config :phoenix_asset_pipeline,
already_compressed_extensions: ~w(.avif .png .webp),
assets_dir: "assets",
static_dir: "priv/static"
already_compressed_extensions, assets_dir, bun_version, manifest_mode, otp_app, and static_dir are
compile-time settings. bun_version must be an exact semantic version and otp_app must match the application
name from mix.exs. manifest_mode defaults to :cached; production builds must set it to :precompiled.
Files matching already_compressed_extensions are served with Cache-Control: no-transform so the HTTP server
does not compress them again dynamically.
SVG sprites
Use svg_sprites to select SVG files outside assets/svg/sprites. Paths are relative to the project root, and
names selects files by basename without requiring literal references in application code:
config :phoenix_asset_pipeline,
svg_sprites: [
%{
file: "flags.svg",
src: "deps/flag_icons/flags/4x3",
names: ~w(ca jp us),
metadata_file: "deps/flag_icons/LICENSE"
}
]
Internal SVG IDs are namespaced by default so references from different source files cannot collide. Set
namespace_ids: false only when every selected SVG is known to contain no internal IDs. When metadata_file is
set, its XML-escaped text is inserted as one root <metadata> element after optimization. Changes to the metadata
file invalidate the SVG cache and trigger development rebuilds.
Build
# Development
mix phx.server
# Production
MIX_ENV=prod mix release
# Manual manifest rebuild
mix phoenix_asset_pipeline.manifest
The development watcher rebuilds changed assets and broadcasts LiveReload events. Production compilation generates PhoenixAssetPipeline.Manifest.Precompiled; separate asset build/deploy tasks are not required.
License
PhoenixAssetPipeline is released under the MIT License. See LICENSE.