Stencil
Compile-time SVG inliner for Phoenix components.
Stencil reads SVG files from a directory at compile time and embeds them into a Phoenix component you can call from HEEx. The running app performs no disk I/O because the raw SVG bytes are baked into the module's beam file. New or changed files re-compile automatically when an SVG changes on disk.
This is a port of Lucky Framework's implementation.
Installation
Add stencil to your mix.exs:
def deps do
[
{:stencil, "~> 0.1.0"}
]
end
Usage
Put your SVGs under priv/svg/ in your app, then create a thin wrapper module
that uses Stencil.Component. The conventional location is
lib/my_app_web/components/inline_svg.ex:
defmodule MyAppWeb.InlineSVG do
use Stencil.Component
end
To make <.inline_svg> available in every HEEx template, import the wrapper
inside MyAppWeb's html_helpers (in lib/my_app_web.ex) alongside
MyAppWeb.CoreComponents:
defp html_helpers do
quote do
# ...
import MyAppWeb.CoreComponents
import MyAppWeb.InlineSVG
# ...
end
end
Now call it from any controller view, LiveView, or component template:
<.inline_svg path="check" />
<.inline_svg path="arrow-right" class="icon" />
<.inline_svg path="icons/star" />
<.inline_svg path="logo" strip_styling={false} />
The path attribute is the file name (or nested path) under priv/svg,
without the .svg extension.
Options
use Stencil.Component, dir: "priv/icons"
:dir— directory to scan, relative to the project root (or an absolute path). Defaults to"priv/svg".
Styling
By default, Stencil strips the SVG's own class, fill, stroke,
stroke-width, and style attributes so page CSS controls the visual. Any
attributes you pass to <.inline_svg> are forwarded onto the wrapping <svg>
tag:
<.inline_svg path="check" class="w-6 h-6 text-emerald-500" />
Pass strip_styling={false} to keep the file's original attributes:
<.inline_svg path="logo" strip_styling={false} />
Stencil also adds a marker attribute so you can target inlined SVGs from CSS:
data-inline-svg="<path>"when styling is strippeddata-inline-svg-styled="<path>"when it is kept
How it works
use Stencil.Component walks the configured directory at macro-expansion time
and generates one function head per SVG:
defp stencil_raw_svg("check", true), do: "<svg …>…</svg>"
Each file is registered as an @external_resource, so the module re-compiles
when any SVG changes. A __mix_recompile__?/0 callback also detects added or
removed files.
License
MIT.