Inline Iconify SVGs for Phoenix and LiveView. Write a normal Phoenix component, let the compiler discover the icons you use, and ship only those icons with your app.
<.icon name="lucide:settings" class="size-5" />PhoenixIconify gives Phoenix apps access to 200,000+ icons from 150+ icon sets without a client-side icon runtime. Browse icons at icon-sets.iconify.design.
Most Iconify integrations load icons in JavaScript. PhoenixIconify keeps icons on the server:
- Icons are discovered from HEEx at compile time
- Only icons you use are fetched and stored
- Rendering is plain inline SVG
- SVG IDs are rewritten to avoid duplicate gradient/mask collisions
- No browser-side icon loader
- Works with LiveView diffs and
phx-*attributes - Dynamic icons can be pre-registered in config
It pairs naturally with Tailwind and Volt-powered Phoenix projects:
<button class="inline-flex items-center gap-2">
<.icon name="lucide:settings" class="size-4" />
Settings
</button>Add the dependency:
def deps do
[
{:phoenix_iconify, "~> 0.3.2"}
]
endAdd the compiler:
def project do
[
compilers: Mix.compilers() ++ [:phoenix_iconify]
]
endImport the component in your web module:
# lib/my_app_web.ex
defp html_helpers do
quote do
import PhoenixIconify, only: [icon: 1]
end
endNow use icons in HEEx:
<.icon name="lucide:settings" class="size-5" />Use Iconify's standard prefix:name format:
<.icon name="lucide:home" class="size-5" />
<.icon name="mdi:account" class="size-6 text-blue-600" />
<.icon name="heroicons:check" class="size-4" />Phoenix-style Heroicons names are supported too:
<.icon name="hero-user" class="size-6" />
<.icon name="hero-sun-mini" class="size-5" />
<.icon name="hero-sun-micro" class="size-4" />Global attributes are forwarded to the SVG, including phx-*, data-*, and aria-*:
<.icon name="lucide:x" class="size-4" phx-click="close" data-testid="close" />Use color for currentColor icon sets and inline when an icon should align with text:
<span>
Saved <.icon name="lucide:check" color="green" inline />
</span>Icons are decorative by default and render with aria-hidden="true":
<.icon name="lucide:settings" class="size-5" />For meaningful icons, provide label or title:
<.icon name="lucide:settings" label="Settings" />
<.icon name="lucide:settings" title="Settings" />Use Tailwind's size-* utilities when possible:
<.icon name="lucide:settings" class="size-5" />PhoenixIconify follows Iconify's dimension behavior. Icons default to 1em high and preserve their aspect ratio. Set one dimension and the other is calculated from the viewBox:
<.icon name="lucide:settings" size="20" />
<.icon name="lucide:settings" height="1em" />
<.icon name="lucide:settings" width="unset" />Iconify aliases can include transformations, and you can transform at render time:
<.icon name="lucide:arrow-right" rotate={1} />
<.icon name="lucide:arrow-right" flip="horizontal" />
<.icon name="lucide:arrow-right" h_flip />
<.icon name="lucide:arrow-right" v_flip />SVG mode is the default. CSS mask/background modes are available for Iconify-style CSS rendering:
<.icon name="lucide:settings" mode="mask" class="size-5" />
<.icon name="logos:elixir" mode="bg" class="size-5" />SVG IDs are replaced automatically, so icons with gradients, masks, clip paths, or animation references can be rendered multiple times on the same page.
- You write
<.icon name="lucide:settings" /> - The
:phoenix_iconifycompiler scans HEEx and~Hsigils - Literal icon names are collected
- Missing icons are fetched through Iconify
- A JSON manifest is written to
priv/iconify/manifest.json - At runtime, the component reads icons from the manifest and renders inline SVG
There is no client-side icon runtime and no JavaScript bundle impact.
Compile-time discovery only works for literal names. If an icon name comes from assigns, a database, or user configuration, register the possible values:
# config/config.exs
config :phoenix_iconify,
extra_icons: [
"lucide:check",
"lucide:x",
"lucide:alert-triangle"
]Then dynamic usage works at runtime:
<.icon name={@status_icon} class="size-4" />config :phoenix_iconify,
extra_icons: ["lucide:check", "lucide:x"],
fallback: "lucide:circle-help",
warn_on_missing: trueOptions:
:extra_icons- icons to include even when they are not found by static discovery:fallback- icon to render when a requested icon is missing:warn_on_missing- log missing icon warnings, enabled by default
PhoenixIconify stores:
priv/iconify/manifest.json- icons used by your apppriv/iconify/sets/*.json- cached icon sets
Useful tasks:
mix phoenix_iconify.prefetch # scan and fetch discovered icons
mix phoenix_iconify.audit # report discovered icons missing from the manifest
mix phoenix_iconify.clean # remove manifest icons no longer used
mix phoenix_iconify.list # list manifest icons
mix phoenix_iconify.stats # show manifest and cache statsCache tasks:
mix phoenix_iconify.cache fetch
mix phoenix_iconify.cache list
mix phoenix_iconify.cache clearFor projects created with Volt, PhoenixIconify is the server-rendered option:
<.icon name="lucide:settings" class="size-5" />It does not use Volt's JavaScript pipeline. If you want client-side icon components instead, use the official npm packages (iconify-icon, @iconify/react, @iconify/vue, etc.) through Volt's normal package handling.
phoenix_iconify ships compile-time discovered, server-rendered Iconify SVGs for Phoenix — 200,000+ icons, zero client runtime.
It is part of a frontend stack that runs inside the BEAM — builds, JS runtimes, icons, and Vue-to-LiveView compilation as supervised parts of the application instead of external toolchain processes. See the Elixir Volt organization for the rest, and Building Blocks for the Future Web for the thesis, architecture, and roadmap that tie them together.
MIT