MaterialSymbols

Phoenix components for Google Material Symbols with a few conveniences and an Apache-2.0 license.

Why this package

Installation

Add material_symbols to your list of dependencies in mix.exs:

def deps do
  [
    {:material_symbols, "~> 1.0.0"}
  ]
end

Requirements

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.