DEV Community

Dominic Jean
Dominic Jean

Posted on

Stop shipping 1,300 icons when you only use 12 — icon tree-shaking for Ionic + Vite

If you use @ionic/core with Vite, you've probably hit this at some point: your build output contains a svg/ folder with every single Ionic icon — all ~1,300 of them — even though your app only uses a dozen.

This happens because Ionic registers icons at runtime by name (<ion-icon name="add-outline" />), so bundlers have no idea which SVG files to keep and which to drop.

I built vite-plugin-ionic-icons to fix exactly that.


What it does

  • Build mode — scans your source files for every <ion-icon name="..."> usage across all frameworks, then emits only those icons as build assets. If you use 12 icons, you ship 12 SVG files.
  • Dev mode — serves icons on-demand directly from node_modules via a middleware. No copying, instant startup.

The difference in bundle output can be dramatic:

Before After
SVG files emitted ~1,300 only what you use
svg/ folder size ~4 MB a few KB

Installation

npm install -D vite-plugin-ionic-icons
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

Requires vite >= 4.0.0 and @ionic/core already installed.


Setup (zero config)

// vite.config.ts
import { defineConfig } from 'vite';
import ionicIcons from 'vite-plugin-ionic-icons';

export default defineConfig({
  plugins: [
    ionicIcons(),
  ],
});
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

That's it. In dev, icons are served at /svg/<name>.svg. In production, only the icons detected in your source are emitted to dist/svg/.


Framework support

The scanner recognizes icon usage across all major frameworks with a set of regex patterns tailored to each syntax.

React / JSX / TSX

<ion-icon name="add-outline" />
<ion-icon name="trash" size="small" />
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

Vue (static & literal dynamic binding)

<ion-icon name="add-outline" />
<ion-icon :name="'add-outline'" />
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

Angular (static & literal property binding)

<ion-icon name="add-outline"></ion-icon>
<ion-icon [name]="'add-outline'"></ion-icon>
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

Svelte

<ion-icon name="add-outline" />
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

Mithril / Preact h()

m('ion-icon', { name: 'add-outline' })
h('ion-icon', { name: 'add-outline' })
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

Plain HTML

<ion-icon name="add-outline"></ion-icon>
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

Dynamic icons

If you resolve icon names at runtime (from an API, user settings, etc.), the static scanner can't detect them. Just list them explicitly:

ionicIcons({
  extraIcons: ['warning-outline', 'checkmark-circle', 'close-circle'],
})
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

All options

ionicIcons({
  // Directory (or array of dirs) to scan. Default: './src'
  srcDir: './src',

  // File extensions to scan. Default: ['.js', '.ts', '.jsx', '.tsx', '.html', '.vue', '.svelte']
  extensions: ['.js', '.ts', '.jsx', '.tsx', '.html', '.vue', '.svelte'],

  // Icons to always include (for dynamic icon names).
  extraIcons: ['warning-outline'],

  // Where Ionic's SVGs live. Default: 'node_modules/@ionic/core/dist/ionic/svg'
  iconSrcDir: 'node_modules/@ionic/core/dist/ionic/svg',

  // Output sub-directory. Default: 'svg'
  iconDestDir: 'svg',

  // Log detected icons and emit count. Default: false
  verbose: true,
})
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

How it works under the hood

Build time
  └── generateBundle()
        ├── scan srcDir recursively
        ├── apply regex patterns (one per framework)
        ├── deduplicate icon names
        └── emitFile() for each used icon → dist/svg/<name>.svg

Dev time
  └── configureServer()
        └── middleware: GET /svg/<name>.svg
              └── pipe from node_modules/@ionic/core/dist/ionic/svg/<name>.svg
⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung ⚡ Match Highlights: dejy/stop shipping icons when you only use icon tree shaking for ionic vite ia - Jadwal Siaran Langsung

The scanner runs once per build/serve session and the result is cached — no repeated file-system work.


Links

Feedback and PRs are very welcome. If you use Ionic with Vite, give it a try and let me know how it goes!

Top comments (0)