vite-plugin-lucide-sprite
v1.0.7
Published
Vite plugin that generates a Lucide SVG sprite from icon ids exported in a Svelte component
Maintainers
Readme
vite-plugin-lucide-sprite
Vite plugin that generates a Lucide SVG sprite (lucide.svg) from icon ids exported in a Svelte component.
Why
This avoids repeating full SVG path markup, and the benefit is not only the ~0.5 KB per-icon overhead: moving SVG paths out of your JavaScript bundle also reduces bundle size and parse/execute cost. Byte for byte, JavaScript is typically your most expensive asset to ship.
- A typical Lucide SVG file is around
0.4-0.5 KBraw:- measured against
[email protected]: median464 B, average480 Bacross1951icons
- measured against
- With a sprite, each icon usage is usually just a short
<use href="...#id">reference - In practice, repeated icon instances often save roughly
~0.4-0.5 KBeach before compression
Exact savings vary by icon, usage count, and gzip/brotli, but a practical rule of thumb is ~0.4-0.5 KB per repeated icon instance.
What it does
- Reads icon ids from a
<script module>export in your component - Loads SVG files from
lucide-static - Generates a single SVG sprite with
<symbol>entries - Serves the sprite in dev and emits it in build
- Caches output in memory and invalidates when the icon component file changes
Requirements
vite >= 5svelte >= 5(compiler is used for AST parsing)lucide-staticinstalled in your app
Install
pnpm add -D vite-plugin-lucide-sprite lucide-staticCodemod CLI
One command upgrade flow (temporary install + migrate):
pnpm dlx vite-plugin-lucide-sprite@latestRun with a locally installed package:
pnpm exec vite-plugin-lucide-sprite --dry-run
pnpm exec vite-plugin-lucide-sprite
pnpm exec vite-plugin-lucide-sprite --forceDefault source directory is ./src; pass --source-dir <dir> if your app uses another directory.
Default Icon path is <source_dir>/lib/components/Icon.svelte.
If <source_dir>/lib/components does not exist, pass --icon-component-path <path>.
By default, CLI also installs missing dependencies (vite-plugin-lucide-sprite, lucide-static@latest).
Use --no-install to skip that step.
Safety behavior:
- By default, the codemod exits if
git status --porcelainis not clean. - Use
--forceto bypass this guard.
What it migrates:
- Replaces mapped
@lucide/sveltecomponent usages with<Icon id="..."> - Updates/cleans
@lucide/svelteimports (rewritten toimport Icon from '$icon') - Removes
@lucide/sveltefromdependencies/devDependencies - Creates
Icon.svelteif missing - Updates matching core files (
<source_dir>/css/base.css,vite.config.js,package.json,tsconfig.json/jsconfig.json)
Icon id mapping:
- Named imports from
@lucide/svelteare converted from component name to kebab-case id. - Default imports from
@lucide/svelte/icons/*use the icon path segment as id. - Special case:
Loader2/LoaderCirclemap toloader-circle.
Quick start
vite.config.js:
import {defineConfig} from 'vite'
import lucide_sprite_plugin from 'vite-plugin-lucide-sprite'
export default defineConfig({
plugins: [lucide_sprite_plugin({icon_component_path: 'src/lib/components/Icon.svelte'})],
})src/lib/components/Icon.svelte (complete example):
<script module>
export const LUCIDE_ICON_IDS = /** @type {const} */ (['check', 'x', 'sun', 'moon'])
</script>
<script>
/** @type {{ id: typeof LUCIDE_ICON_IDS[number], size?: number|string, color?: string, [x: string]: any }} */
let {id, size = 24, color = 'currentColor', ...rest} = $props()
const sprite_href = $derived(`${import.meta.env.BASE_URL}lucide.svg#${id}`)
</script>
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke={color}
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
{...rest}
>
<use href={sprite_href}></use>
</svg>Use it in app code:
<Icon id="check" />If you are rendering <use> directly (without an Icon wrapper), define the href in script first:
<script>
const sprite_href = `${import.meta.env.BASE_URL}lucide.svg#check`
</script>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<use href={sprite_href}></use>
</svg>Options
type LucideSpritePluginOptions = {
icon_component_path?: string
icon_ids_export_name?: string
output_file_name?: string
minify?: boolean
}icon_component_path:- Default:
'src/components/Icon.svelte' - Path to the Svelte component that exports icon ids
- Default:
icon_ids_export_name:- Default:
'LUCIDE_ICON_IDS' - Name of the
<script module>export to read
- Default:
output_file_name:- Default:
'lucide.svg' - Output file name (served in dev and emitted in build)
- Default:
minify:- Default:
true - Minifies generated sprite markup (collapses line breaks/indentation)
- Default:
Example with custom values:
lucide_sprite_plugin({
icon_component_path: 'src/lib/ui/Icon.svelte',
icon_ids_export_name: 'APP_LUCIDE_ICONS',
output_file_name: 'assets/lucide.svg',
minify: true,
})Rules for the icon export
The plugin intentionally keeps parsing simple and strict:
- Export must exist in
<script module> - Export value must be an array literal
- Every item must be a string literal
Accepted:
<script module>
export const LUCIDE_ICON_IDS = ['check', 'x']
</script>Rejected:
<script module>
const base = ['check']
export const LUCIDE_ICON_IDS = [...base, dynamic_icon]
</script>Dev and build behavior
- Dev:
- Middleware responds to
/lucide.svg(or your customoutput_file_name) - Regeneration is lazy (on first request after invalidation)
- Middleware responds to
- Build:
- Emits the sprite as an asset through Rollup/Vite (
generateBundle)
- Emits the sprite as an asset through Rollup/Vite (
- Caching:
- Cached in memory
- Invalidated when the configured icon component file changes
- Output details:
- IDs are deduplicated with
Set - IDs are sorted before generation
- Sprite markup is minified by default
- IDs are deduplicated with
Troubleshooting
must export LUCIDE_ICON_IDS from <script module>:- Add the export in
<script module>, not instance<script>
- Add the export in
must be an array literal:- Use a direct array literal instead of computed values
must be a string literal:- Use plain strings (not variables, expressions, or function calls)
Lucide icon "..." was not found:- Ensure the icon id exists in your installed
lucide-staticversion
- Ensure the icon id exists in your installed
TypeScript
The package is authored in TypeScript and ships:
dist/index.jsdist/index.d.ts
