Icons in Svelte 5
glyphfox/svelte ships one Svelte 5 component per icon, plus Icon, Fusion and MorphIcon, with the same props as every other glyphfox package.
Install
npm install glyphfox
The components are .svelte source files that your app compiles, so the build needs Svelte 5 and its compiler, usually through vite-plugin-svelte, which every SvelteKit project already has. They are written with runes such as $props and $state, so Svelte 4 cannot compile them.
svelte 5 is an optional peer dependency of glyphfox: you only need it when you import glyphfox/svelte. The package exposes it under the svelte export condition, which vite-plugin-svelte uses to recognize a Svelte library and compile its components for the browser and the server.
Use the components
<script> import { HomeIcon, SearchIcon, BellIcon } from 'glyphfox/svelte'; let { unread = 0 } = $props(); </script> <nav class="toolbar"> <a href="/" aria-label="Home"><HomeIcon /></a> <SearchIcon size={20} weight={1.5} class="muted" /> <BellIcon variant="duotone" secondaryColor="#ff7a1a" modifier={unread ? 'dot' : undefined} /> </nav>
Each icon is a component named after the icon in PascalCase with an Icon suffix: arrow-right becomes ArrowRightIcon, loader-circle becomes LoaderCircleIcon. Props use the camelCase names, exactly as in JavaScript, because Svelte passes props to components as written.
class and style are applied to the svg, and any other attribute, such as aria-*, data-* or id, is spread onto it. Event handlers are props in Svelte 5: onclick, onmouseenter and onmouseleave are forwarded, and they still run when a click or hover trigger is set on the icon.
Strokes use currentColor, so the icon follows the CSS color of its parent. The color prop sets a fixed color on the svg.
Weight, corners and style
weight sets the stroke from 1 to 3, corners switches between round and sharp joins, and variant picks outline, duotone or solid. Below 24 px the stroke is thickened slightly so small icons keep their presence; absoluteWeight keeps the exact value.
<script> import { FolderIcon } from 'glyphfox/svelte'; </script> <FolderIcon weight={1} /> <FolderIcon weight={3} corners="sharp" /> <FolderIcon variant="duotone" secondaryColor="#ff7a1a" /> <FolderIcon variant="solid" /> <FolderIcon size={16} weight={1.5} absoluteWeight />
Icon, modifiers and fusion
Icon renders any spec of the name syntax: a name, a modifier badge (file+check, with @tl, @tr, @bl or @br for the corner), a fusion (cloud&lock:overlap) or a style (heart/solid). Fusion takes a list of names and a layout: overlap, side, stack, grid, orbit, badge or inside.
Icon components carry their own data: each one registers its icon in a <script module> block. A name inside a string has no import behind it, so register those once with registerIcons. Modifier badges are drawn by the engine and need no registration.
import { registerIcons } from 'glyphfox/svelte'; import file from 'glyphfox/icons/file'; import cloud from 'glyphfox/icons/cloud'; import lock from 'glyphfox/icons/lock'; import menu from 'glyphfox/icons/menu'; import x from 'glyphfox/icons/x'; import play from 'glyphfox/icons/play'; import pause from 'glyphfox/icons/pause'; // Every name used in a string: Icon, Fusion, MorphIcon, morphStore and morphTo. registerIcons([file, cloud, lock, menu, x, play, pause]);
<script> import './lib/icons.js'; import { Icon, Fusion } from 'glyphfox/svelte'; let { saved = false } = $props(); </script> <Icon name={saved ? 'file+check' : 'file'} /> <Icon name="file+alert@tr" variant="duotone" /> <Fusion icons={['cloud', 'lock']} layout="overlap" gap={1} />
Import the registration module from your entry file or root component. A component can also register in its own <script module> block, which runs once when the module loads rather than once per instance.
MorphIcon, morphTo and morphStore
MorphIcon animates whenever its name changes, which pairs naturally with $state:
<script> import { MorphIcon } from 'glyphfox/svelte'; let open = $state(false); </script> <button type="button" aria-label="Menu" aria-expanded={open} onclick={() => (open = !open)}> <MorphIcon name={open ? 'x' : 'menu'} duration={300} /> </button>
Any icon component also takes morphTo with a trigger: click toggles on every click, hover follows the pointer, state follows active, and the default mount morphs once after mounting.
<script> import { HeartIcon, PlayIcon } from 'glyphfox/svelte'; let { playing = false } = $props(); </script> <HeartIcon morphTo="heart/solid" trigger="click" /> <PlayIcon morphTo="pause" trigger="state" active={playing} />
morphStore(initial, options) is a store for custom rendering. Call set(name) to start a morph and read $store, a frame with icon and variant that you can pass to Icon.
<script> import './lib/icons.js'; import { Icon, morphStore } from 'glyphfox/svelte'; const frame = morphStore('play', { duration: 250 }); let playing = $state(false); function toggle() { playing = !playing; frame.set(playing ? 'pause' : 'play'); } </script> <button type="button" aria-label="Play" aria-pressed={playing} onclick={toggle}> {#if $frame}<Icon icon={$frame.icon} variant={$frame.variant} />{/if} </button>
Hand tuned pairs cover menu to x, play to pause, plus to check, sun to moon, eye to eye-off, bell to bell-ring, heart to heart/solid, copy to check, lock to unlock and wifi to wifi-off. Other pairs morph with an automatic match.
State driven animations
Living states are presets that understand the icon: draw traces the strokes in drawing order, shake swings from the top, and pop, wiggle, spin, pulse and bounce complete the set. With trigger="state" an animation plays while active is true, so it follows your runes directly.
<script> import { BellIcon, LoaderCircleIcon, SparklesIcon } from 'glyphfox/svelte'; let { hasNews = false, loading = false } = $props(); </script> <BellIcon animate="shake" trigger="state" active={hasNews} /> {#if loading}<LoaderCircleIcon animate="spin" />{/if} <SparklesIcon animate="draw" trigger="hover" />
Each preset is a small stylesheet inside the svg, so it needs no CSS import and stops for visitors who prefer reduced motion. The animated icons guide covers durations, delays and loops.
Deep imports and types
glyphfox/svelte re-exports every component, and production builds drop the ones you do not import. A deep import resolves to a single .svelte file, which keeps the code your compiler and dev server see down to what you use:
<script> import FoxIcon from 'glyphfox/svelte/icons/fox'; </script> <FoxIcon size={32} variant="duotone" title="glyphfox" />
Types ship with the package. Every component is a Component<GlyphProps>, where GlyphProps is the shared props plus any SVG attribute, so a typed wrapper can forward everything:
<script lang="ts"> import { Icon, type GlyphProps } from 'glyphfox/svelte'; import type { IconName } from 'glyphfox'; let { name, ...rest }: GlyphProps & { name: IconName } = $props(); </script> <Icon {name} weight={1.75} {...rest} />
Names typed as IconName are checked at compile time, but they still need to be registered to render: see the pitfalls below and the tree shaking guide.
Props
Every icon component, Icon, Fusion and MorphIcon takes the shared props, in camelCase.
| Prop | Type | Default | Description |
|---|---|---|---|
size | number, string | 24 | Width and height; numbers are pixels. |
color | string | currentColor | Main color. |
secondaryColor | string | color | Color of the duotone layer and of the solid fill. |
secondaryOpacity | number | 0.3 / 1 | Opacity of the duotone layer or of the solid fill. |
weight | number | 2 | Stroke from 1 to 3 on the 24 grid, optically compensated below 24 px. |
absoluteWeight | boolean | false | Keeps the stroke at weight pixels at any size. |
corners | round, sharp | round | Round or sharp joins and caps. |
variant | outline, duotone, solid | outline | Style axis; solid comes from the closed shapes. |
rotate | number | 0 | Rotation in degrees. |
flipX | boolean | false | Mirror horizontally. |
flipY | boolean | false | Mirror vertically. |
mirrorRtl | boolean | false | Mirror in right to left layouts. |
animate | draw, pop, wiggle, spin, pulse, bounce, shake | none | Living state preset. |
trigger | mount, hover, click, state | mount | When the animation or the morph plays. |
active | boolean | false | The state for trigger state. |
duration | number (ms) | per preset | Animation or morph duration. |
delay | number (ms) | 0 | Animation or morph delay. |
loop | boolean, number | false | Repeat forever or a number of times (spin and pulse loop by default). |
modifier | plus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down, dot | none | Badge with an automatic knockout gap. |
modifierCorner | top-left, top-right, bottom-left, bottom-right | bottom-right | Corner of the badge. |
morphTo | name, data | none | Morph target; the trigger decides when it shows. |
title | string | none | Accessible name: title, role img and aria-label. |
decorative | boolean | true without a title | Hides the icon from assistive technology. |
Icon adds name, icon (icon data) and gap. Fusion adds icons, layout, gap, scale and offsets. MorphIcon adds name and icon. Children render inside the svg.
Pitfalls
- Kebab-case props do nothing
secondary-color="#ff7a1a"is not thesecondaryColorprop in Svelte: it is spread onto the svg as a plain attribute, and the icon keeps its default. Use the camelCase names everywhere.- Names that work in development and vanish in production
- During development Vite pre-bundles the package and every name resolves. A production build keeps only what you import, and Vite does not bundle the on demand loader inside
node_modules, so unregistered names inIcon,Fusion,MorphIcon,morphToandmorphStorerender an empty svg. Register them. - morphStore created before registration
morphStoreresolves a name when you callset. If the name is not registered at that moment, it waits for the loader instead. Import the registration module before you create the store, as in the example above.- Star imports and dynamic lookups
import * as icons from 'glyphfox/svelte'withicons[name]keeps every component in the bundle. Map the components you need, or useIconwith registered names.
Questions
Can I use glyphfox in a Svelte 4 project?
Not with glyphfox/svelte, which needs the Svelte 5 compiler. In a Svelte 4 app the <glyph-fox> web component works with the same props as attributes; see the web components guide.
Do the components need SvelteKit?
No. Any Svelte 5 setup that compiles .svelte files works, such as plain Vite with vite-plugin-svelte. SvelteKit adds server rendering and prerendering, covered in the SvelteKit guide.