Phoenix components for Google Material Symbols with a few conveniences and an Apache-2.0 license.
Why this package
- Uses the current Material Symbols set: 3,945 icons in outlined, rounded, and sharp styles, with filled variants. The package contains all three styles; the default build compiles outlined only. Material Symbols is the current set, introduced in April 2022; Google describes the legacy Material Icons set as no longer updated.
- Ships under Apache-2.0 and contains no AGPL-3.0 code. The existing Elixir
material_iconspackage is AGPL-3.0, which carries copyleft and network-use obligations. - Vendors the SVGs at release time. There is no HTTP client and no icon fetch
during
mix deps.getor compilation, so CI needs no extra network step. - Adds no runtime dependencies beyond Phoenix LiveView and
phoenix_html.
Installation
Add material_symbols to your list of dependencies in mix.exs:
def deps do
[
{:material_symbols, "~> 1.0.0"}
]
endRequirements
- Elixir
~> 1.15 - Phoenix LiveView
~> 1.0 phoenix_html ~> 4.0
Usage
<MaterialSymbols.home />
<MaterialSymbols.home style="outlined" />
<MaterialSymbols.home fill />
<MaterialSymbols.home size={32} class="text-blue-600" />
<MaterialSymbols.render name="home" style="outlined" />
<MaterialSymbols.icon name={@icon} size={20} class="size-5" />style selects the icon style and must be one of the compiled styles. fill
selects the filled variant. The SVG uses fill="currentColor", so set the
color with class or with the surrounding CSS. The style HTML attribute is
reserved for the icon style; use class for styling.
fill is a boolean, size is a positive integer, and style is a string.
Use fill and size={32}; string values such as fill="true" and
size="32" raise, and so does an atom style such as style={:rounded}.
Explicit width, height, or viewBox attributes override the generated
defaults.
Per-icon components pass arbitrary attributes to the <svg> element.
render/1 and icon/1 accept global attributes such as aria-*, data-*,
id, and role, plus width, height, viewBox, xmlns,
preserveAspectRatio, and focusable. The default size is 24 pixels; size
sets both width and height.
The <svg> defaults to aria-hidden="true". For an icon that carries meaning
on its own, override it:
<MaterialSymbols.home aria-hidden="false" aria-label="Home" role="img" />Dynamic names
Both MaterialSymbols.render/1 and MaterialSymbols.icon/1 take a name plus
the same style, fill, size, class, and global attributes as the per-icon
components. They differ in what happens when the name is not compiled:
| Component | Unknown name |
|---|---|
MaterialSymbols.render/1 | renders <!-- icon x not found --> |
MaterialSymbols.icon/1 | raises ArgumentError |
Use render/1 when the name is optional or user-controlled and the page should
still render. Use icon/1 when the name comes from controlled data and an
unknown name is a bug.
<MaterialSymbols.render name={@icon} />
<MaterialSymbols.icon name={@icon} size={20} class="size-5" />Use MaterialSymbols.icon_names/0 to list every compiled component name and
MaterialSymbols.search_icons/1 to filter names. Both return component-name
atoms. Use MaterialSymbols.icon_name/1 for the upstream name;
Atom.to_string/1 is not that name, because 9k compiles to :icon_9k. The per-icon
components are intentionally omitted from the ExDoc function list; browse the
upstream set at fonts.google.com/icons or use
icon_names/0. Icon names that start with a digit get an icon_ prefix in the
generated component name, so 9k is <MaterialSymbols.icon_9k />. The
original name still works with both dynamic components:
<MaterialSymbols.render name="9k" /> or
<MaterialSymbols.icon name="9k" />. The component name also works:
<MaterialSymbols.icon name="icon_9k" /> or name={:icon_9k}.
MaterialSymbols.icon_name/1 maps a generated component atom back to the
upstream name: MaterialSymbols.icon_name(:icon_9k) returns "9k".
Compile-time configuration
By default, all 3,945 icons are available and only the outlined style is
compiled. Include more styles:
config :material_symbols, styles: [:outlined, :rounded, :sharp]Compile only selected icons:
config :material_symbols, icons: [:home, :menu, :settings]styles defaults to [:outlined]; :all includes all three. icons defaults
to :all. Use the upstream icon name in icons; the digit-leading icon 9k
is configured as "9k" even though its component is icon_9k. Component names
such as :icon_9k are also accepted. Unknown icon or style names fail the
build. This is compile-time configuration, so it
belongs in config/config.exs or the environment config, not runtime.exs;
changing it recompiles the dependency. If outlined is not configured, the
first configured style becomes the default. The build also fails if a selected
icon lacks one of the requested style/fill variants.
Finding used icons
The package includes a static usage scanner:
mix material_symbols.used
mix material_symbols.used --paths lib,test
mix material_symbols.used lib web
It scans lib by default, prints the icons it found with file:line
locations, and suggests icons and styles configuration. It detects
qualified components, aliases in .ex/.exs files, render/1 and icon/1
literals, function captures, and apply/3. Template files are scanned for
fully qualified MaterialSymbols. names. Comments, documentation heredocs,
ordinary string literals, and sigils other than ~H are ignored. An unstyled
call counts as the configured default style (outlined when compiled,
otherwise the first configured style). Dynamic names and
icon_names/0/raw_names/0/icon_name/1/search_icons/1 produce warnings,
and import MaterialSymbols is flagged because imported <.icon> usage is not
detected. The task does not edit configuration.
Run it before deployment to trim the production binary. The scan is static; review the reported locations before applying the allowlist.
Vendored icons
The SVGs are copied from @material-symbols/svg-400@0.49.0: 23,670 files
covering 3,945 icons in three styles with filled and unfilled variants. The
repository keeps them under priv/icons/; the Hex package ships them as
priv/icons.tar.gz because Hex limits metadata.config to 1 MB. The archive
always contains all three styles; the compile-time configuration above decides
what is generated. See priv/SOURCE.md for the source URL, checksums, license,
and rebuild command.
Provenance
See NOTICE.md for the full provenance review.
Performance
The icon path data is compiled into MaterialSymbols, so rendering does no file
I/O. The compiled size and compile time depend on the configuration:
| Build | Approximate BEAM |
|---|---|
| Default: outlined, all 3,945 icons | ~7.8 MB |
| All three styles, all icons | ~17 MB |
| Allowlist: 2 icons, outlined | ~119 KB |
The all-styles build takes about 14 seconds to compile in a consumer; the
two-icon allowlist takes about 1.4 seconds. The narrow allowlist still carries
the full raw_names/0 list, which the usage task needs to recognize icons that
are vendored but not compiled.
Development
mix deps.get
mix test
mix docs
The package's own test build compiles all three styles through
config/config.exs; the dev build uses the default outlined-only build. The
icon archive is rebuilt from priv/icons/ with the command in
priv/SOURCE.md.
License
The package code is original and licensed under the Apache License 2.0. See LICENSE.
The Material Symbols icons are designed by Google and distributed under the
Apache License 2.0. The vendored copy includes the upstream license at
priv/icons/LICENSE; provenance, version, and checksums are in
priv/SOURCE.md.
Apache-2.0 is permissive: it allows commercial and closed-source use, modification, and redistribution, provided the license and notices are retained. This package is not affiliated with or endorsed by Google.
See NOTICE.md for the provenance review and the relationship to
the AGPL-3.0 material_icons package.