Skip to content

yermartee/material-symbols-cli

Repository files navigation

material-symbols-cli logo

material-symbols-cli

Fetch Google Material Symbols and generate tree-shakable, framework-native icon components.

npm version npm downloads bundle size license TypeScript Node.js

Quick Start · Why msym? · 13 Formats · Config · CLI · API


Why msym?

Existing Material Symbols packages ship every icon upfront — fonts, CSS bundles, or massive SVG dumps. You install 12 MB, configure webpack loaders, and still don't get framework-native components.

msym takes a different approach: you declare exactly which icons you need, and it generates optimized, tree-shakable, framework-native components on demand.

Feature msym material-symbols @material-symbols/svg-* react-material-symbols
Install size ~190 KB (CLI only) 12.7 MB 12.8 MB (23K files) 1.2 MB
Icons shipped Only what you use All 3000+ All 3000+ All (font)
Formats 13 (MUI, React, React Native, Vue, Angular, Svelte, Solid, Preact, Qwik, Lit, WC, Flutter, SVG) CSS only Raw SVG (needs @svgr) React only
Tree-shakable Yes (individual files) No (font/CSS) Manual No (font)
Component generation Native per-framework No No No
Variable axes Fill, weight, grade, optical size CSS font-variation-settings Fill only Font settings
SVGO optimization Built-in No Yes No
Config file TypeScript with autocomplete No No No
Naming customization 4 conventions + prefix/suffix/overrides No No No
Programmatic API Full No No No
Generated output deps Target framework only Font files SVG files Font files

Quick Start

# Install
pnpm add -D material-symbols-cli    # or npm / yarn / bun

# Create config
msym init

# Generate icons
msym sync

That's it. Three commands.

The package exposes two executable names: msym and material-symbols. The examples below use msym.

Install

# pnpm (recommended)
pnpm add -D material-symbols-cli

# npm
npm install -D material-symbols-cli

# yarn
yarn add -D material-symbols-cli

# bun
bun add -D material-symbols-cli

Configuration

Create material-symbols.config.mts in your project root:

import { defineConfig } from "material-symbols-cli/config";

export default defineConfig({
  icons: ["home", "search", "menu", "close", "settings", "arrow_back"],
  format: "mui",
  style: "outlined",
  outputDir: "src/icons",

  variants: [
    { fill: 0, weight: 400, grade: 0, opticalSize: 24 },
    { fill: 1 },
  ],

  naming: {
    convention: "PascalCase",
    prefix: "",
    suffix: "",
    overrides: {
      "3d_rotation": "Rotate3D",
    },
  },

  defaultProps: {
    size: 24,
    color: "currentColor",
  },

  optimize: true,
  useClientDirective: true,
  barrel: true,
  concurrency: 25,
});

All options have sensible defaults. The minimal config is:

export default defineConfig({
  icons: ["home", "search"],
});
Full configuration reference
Option Type Default Description
icons "all" | string[] Icons to generate. "all" for every available symbol (3000+).
format OutputFormat "mui" Output framework format.
style "outlined" | "rounded" | "sharp" "outlined" Google Material Symbols style family.
outputDir string "src/icons" Output directory for generated files.
variants SymbolVariant[] [{ fill: 0, weight: 400 }] Axis variants to generate per icon.
naming NamingConfig { convention: "PascalCase" } Naming conventions for files and exports.
defaultProps object { size: 24, color: "currentColor" } Default props for generated components.
optimize boolean true SVGO optimization.
useClientDirective boolean true "use client" directive for Next.js App Router.
barrel boolean true Generate barrel index.ts file.
extension string Auto-detected Override file extension.
declarations boolean false Generate .d.ts alongside components.
concurrency number 25 Parallel download limit.
header string | false "Auto-generated…" Header comment in generated files.

CLI Commands

msym init

Alias: material-symbols init

Create a starter config file with TypeScript autocompletion.

msym init
msym init --format vue

msym sync

Alias: material-symbols sync

Fetch icons from Google and generate components based on your config.

msym sync
msym sync --format react --output src/components/icons
msym sync --icons home,search,menu --style rounded

Alias: msym generate

msym list

Alias: material-symbols list

List all 3000+ available Material Symbol names.

msym list
msym list --search arrow
msym list --json

msym add <icons...>

Alias: material-symbols add <icons...>

Add specific icons to your output directory without touching existing ones.

msym add favorite star bookmark

msym remove <icons...>

Alias: material-symbols remove <icons...>

Remove specific icons from your output directory.

msym remove favorite star

Output Formats

Format Extension Framework Generated Code
mui .tsx React + MUI createSvgIcon wrapper
react .tsx React 18/19 Standalone SVG component with props
react-native .tsx React Native react-native-svg component
vue .vue Vue 3 SFC with <script setup>
angular .ts Angular 17+ Standalone component
svelte .svelte Svelte 5 Component with $props()
solid .tsx SolidJS Function component
preact .tsx Preact Function component
qwik .tsx Qwik component$()
lit .ts Lit 3 LitElement custom element
web-component .ts Vanilla Custom Element with Shadow DOM
flutter .dart Flutter flutter_svg widget
svg .svg Any Raw optimized SVG

Framework Examples

React + MUI

import HomeIcon from "./icons/Home";
import SearchIcon from "./icons/Search";

function App() {
  return (
    <>
      <HomeIcon sx={{ fontSize: 32 }} />
      <SearchIcon color="primary" />
    </>
  );
}

React (Standalone)

import Home from "./icons/Home";

<Home size={32} color="#333" className="nav-icon" />

React Native

import Home from "./icons/Home";

<Home size={32} color="#333" />

Vue 3

<script setup>
import Home from "./icons/Home.vue";
</script>

<template>
  <Home :size="32" color="#333" />
</template>

Angular

import { HomeComponent } from "./icons/Home";

@Component({
  imports: [HomeComponent],
  template: `<home [size]="32" color="#333" />`
})
export class AppComponent {}

Svelte 5

<script>
  import Home from "./icons/Home.svelte";
</script>

<Home size={32} color="#333" />

SolidJS

import Home from "./icons/Home";

<Home size={32} color="#333" />

Preact

import Home from "./icons/Home";

<Home size={32} color="#333" />

Qwik

import { Home } from "./icons/Home";

export default component$(() => (
  <Home size={32} color="#333" />
));

Lit

import "./icons/Home";

html`<home-icon size="32" color="#333"></home-icon>`;

Web Component (Vanilla)

<script type="module" src="./icons/Home.js"></script>
<home-icon size="32" color="#333"></home-icon>

Flutter

import 'icons/home.dart';

const Home(size: 32, color: Color(0xFF333333));

Naming Conventions

Convention Input Component Filename
PascalCase arrow_back ArrowBack ArrowBack.tsx
camelCase arrow_back arrowBack arrowBack.tsx
kebab-case arrow_back arrow-back arrow-back.tsx
snake_case arrow_back arrow_back arrow_back.tsx

With prefix "Icon" and suffix "Symbol":

arrow_back → IconArrowBackSymbol / IconArrowBackSymbol.tsx

Leading digits are automatically converted to words:

3d_rotation → ThreeDRotation
10k         → TenK

Programmatic API

Use the full pipeline programmatically in build scripts or custom tooling:

import {
  defineConfig,
  resolveConfig,
  fetchSymbolNames,
  fetchIcons,
  getGenerator,
  processRawSvg,
  generateBarrel,
  resolveComponentName,
  resolveFileName,
  variantSuffix,
} from "material-symbols-cli";

const config = resolveConfig({
  icons: ["home", "search"],
  format: "react",
});

const names = await fetchSymbolNames();
const svgs = await fetchIcons(["home"], config, (done, total) => {
  console.log(`${done}/${total}`);
});

const generator = getGenerator(config.format);
for (const svg of svgs) {
  const { pathData } = processRawSvg(svg.svgContent);
  const code = generator("Home", pathData, config);
}

Tree-Shaking

Generated code is tree-shakable by design:

  • One file per icon — bundlers eliminate unused icons at build time
  • Named re-exports — barrel files use export { X } from "./X" syntax
  • "sideEffects": false — declared in generated package.json context
  • Format-specific imports only — generated files only import what their target framework requires

Integration Tips

Next.js App Router

useClientDirective: true (default) adds "use client" to React/MUI/Solid/Preact components, making them compatible with Next.js App Router server components.

Monorepo

Place your config at the repo root and set outputDir to the target package:

export default defineConfig({
  icons: ["home"],
  outputDir: "packages/ui/src/icons",
});

CI/CD

Add msym sync to your CI pipeline to keep icons in sync:

- run: npx msym sync
- run: git diff --exit-code src/icons/

Contributing

See CONTRIBUTING.md for development setup and contribution guidelines.

License

Apache-2.0 — the same license Google uses for Material Symbols.

Generated icon SVGs are sourced from Google's material-design-icons repository and are also Apache-2.0 licensed. You are free to use, modify, and redistribute the generated components in any project.


About

CLI tool to fetch Google Material Symbols and generate tree-shakable icon components for React, React Native, MUI, Vue, Angular, Svelte, Solid, Preact, Qwik, Lit, Web Components, Flutter, and raw SVG

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors