Current section
Files
Jump to
Current section
Files
README.md
# Stencil
[](https://codeberg.org/w0u7/stencil/actions?workflow=ci.yml)
[](https://codeberg.org/w0u7/stencil/tags)
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](https://luckyframework.org/guides/frontend/rendering-html#adding-attributes-to-inline-svgs).
## Installation
Add `stencil` to your `mix.exs`:
```elixir
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`:
```elixir
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`:
```elixir
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:
```heex
<.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
```elixir
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:
```heex
<.inline_svg path="check" class="w-6 h-6 text-emerald-500" />
```
Pass `strip_styling={false}` to keep the file's original attributes:
```heex
<.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 stripped
- `data-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:
```elixir
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.