React icons
Every glyphfox icon is a React component that renders inline SVG. The same props work on every icon, nothing needs a provider, and the components you do not import never reach your bundle.
Install
The React components live in the main glyphfox package under the glyphfox/react entry point. React is an optional peer dependency: the package works with React 16.8 and newer because it only needs hooks and forwardRef, and its test suite runs against React 19.
npm install glyphfox
There is no stylesheet to import and no build plugin to add. The package ships ESM and CommonJS builds with type declarations, so Vite, webpack, Rspack, esbuild and Parcel pick it up as is.
Render your first icons
Each icon is exported under its name in PascalCase with an Icon suffix: home becomes HomeIcon, arrow-right becomes ArrowRightIcon and message-circle becomes MessageCircleIcon. Search the icon browser to find a name; every icon page, such as arrow right, also shows the import line.
import { HomeIcon, SearchIcon, BellIcon } from 'glyphfox/react'; export function Toolbar() { return ( <nav className="toolbar"> <a href="/"><HomeIcon /> Home</a> <button type="button" aria-label="Search"> <SearchIcon /> </button> <BellIcon size={20} /> </nav> ); }
Without props an icon is 24 by 24 pixels, draws with stroke="currentColor" so it takes the text color of its parent, and is marked aria-hidden="true" because most icons sit next to a visible label. Give it a title when the icon is the only thing that carries the meaning; the accessibility guide covers the patterns.
Size, color, weight and style
Every icon renders along three axes: weight, corners and style. They are plain props, so they can follow state, themes or breakpoints like any other value.
import { FolderIcon, SearchIcon, ArrowRightIcon } from 'glyphfox/react'; export function Axes() { return ( <> <SearchIcon size={32} color="#ff7a1a" /> <SearchIcon weight={1} /> <SearchIcon weight={3} corners="sharp" /> <SearchIcon size={16} weight={1.5} absoluteWeight /> <FolderIcon variant="duotone" secondaryColor="#ff7a1a" /> <FolderIcon variant="solid" /> <ArrowRightIcon rotate={90} /> <ArrowRightIcon mirrorRtl /> </> ); }
sizetakes a number of pixels or any CSS length string such as"1.25em".weightruns from 1 to 3 on the 24 grid. Below 24 pixels the stroke is compensated optically, up to 30 percent heavier at the smallest sizes, so a 16 pixel icon keeps the same presence as a 24 pixel one.absoluteWeightturns that off and keeps the stroke at exactlyweightpixels.variant="duotone"uses the hand drawn second layer when the icon has one, or a translucent fill of its closed shapes.variant="solid"fills the closed shapes and cuts the inner lines out as negative space.mirrorRtlflips directional icons when they render insidedir="rtl", so the same markup works in both directions.
Icons by name
When the icon comes from data, a CMS field, a route table or a user setting, render it with Icon and a name. The name prop takes the full name syntax shared by every glyphfox package, so modifiers, fusions and styles work from a single string.
import { Icon } from 'glyphfox/react'; export function MenuItem({ item }) { // item.icon could be "settings", "file+check@tr", "cloud&lock:overlap" or "heart/solid" return ( <li> <Icon name={item.icon} size={18} /> {item.label} </li> ); }
Names that were not imported anywhere are loaded on demand with a dynamic import. webpack and esbuild split it into one small chunk per icon, and until that chunk arrives Icon renders an empty SVG of the right size, so the layout does not jump. A Vite production build leaves that import untouched inside node_modules, so there the name stays empty (the tree shaking guide has the details). In every bundler the fix is the same: register the data of the names you render from strings once at startup, and they render synchronously, on the first paint and on the server:
import { registerIcons } from 'glyphfox/react'; import { iconSettings, iconFile, iconCloud, iconLock } from 'glyphfox'; registerIcons([iconSettings, iconFile, iconCloud, iconLock]);
The data exports (iconSettings, iconArrowRight...) come from the main glyphfox entry and are tree shaken like the components: importing one costs a few hundred bytes. Importing a component such as SettingsIcon registers its data as well.
Modifiers and fusions
A modifier is a small badge in a corner of any icon: plus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down or dot. The icon underneath is cut around the badge with a knockout gap, so lines never touch at any weight.
import { FileIcon, CloudIcon, Fusion } from 'glyphfox/react'; export function Status({ saved, syncing }) { return ( <> <FileIcon modifier={saved ? 'check' : undefined} /> <CloudIcon modifier={syncing ? 'sync' : undefined} modifierCorner="top-right" /> <Fusion icons={['cloud', 'lock']} layout="overlap" gap={1} /> <Fusion icons={['shield', 'heart']} layout="inside" /> </> ); }
Fusion combines 2 to 10 icons into one new icon that still follows the icon rules: one weight, the 24 grid and the padding box. The layouts are overlap, side, stack, grid, orbit, badge and inside; gap sets the knockout gap in grid units, and scale and offsets fine tune each part. Fusion accepts every shared prop, so a fused icon can be duotone, animated or labelled like any other.
Morphing and living states
Three tools cover motion. MorphIcon morphs whenever its name changes. morphTo on any icon morphs toward a target when its trigger fires. animate plays a preset (draw, pop, wiggle, spin, pulse, bounce or shake) on mount, hover, click or while active is true.
import { MorphIcon, MenuIcon, BellIcon, HeartIcon } from 'glyphfox/react'; export function Motion({ open, copied, hasNews, liked }) { return ( <> <MorphIcon name={copied ? 'check' : 'copy'} duration={300} /> <MenuIcon morphTo={open ? 'x' : undefined} /> <BellIcon animate="shake" trigger="state" active={hasNews} /> <HeartIcon animate="pop" trigger="click" variant={liked ? 'solid' : 'outline'} /> </> ); }
For a custom renderer, useMorph(name, { duration }) returns the icon data of the current frame, which you can pass to Icon through its icon prop or to your own drawing code. Every animation respects prefers-reduced-motion. The animated icons guide goes through presets, triggers and the hand tuned morph pairs.
import { Icon, useMorph } from 'glyphfox/react'; export function PlayButton({ playing }) { const frame = useMorph(playing ? 'pause' : 'play', { duration: 250 }); return frame ? <Icon icon={frame.icon} variant={frame.variant} size={28} /> : null; }
Refs, className, style and events
The components are forwardRef components, so a ref points at the rendered <svg> element. className is added after the built in glyphfox glyphfox-NAME classes, style is passed as is, and every other SVG attribute or event handler (onClick, data-*, aria-*, id) goes straight to the element.
import { useRef } from 'react'; import { HeartIcon } from 'glyphfox/react'; export function Like({ onLike }) { const ref = useRef(null); const like = () => { ref.current?.animate([{ transform: 'scale(1)' }, { transform: 'scale(1.25)' }, { transform: 'scale(1)' }], 240); onLike(); }; return ( <button type="button" onClick={like}> <HeartIcon ref={ref} className="like-icon" style={{ color: 'var(--accent)' }} /> Like </button> ); }
An aria-label passed this way also marks the icon as meaningful: the component switches from aria-hidden to role="img" automatically.
TypeScript
Types ship with the package. GlyphIconComponent is the type of every icon component, GlyphProps are its props (the shared props plus every SVG attribute), and IconName from the main entry is the union of every icon name, which gives you autocompletion on Icon and in your own data.
import type { IconName } from 'glyphfox'; import { Icon, HomeIcon, SettingsIcon, type GlyphIconComponent } from 'glyphfox/react'; type NavItem = { label: string; href: string; icon: GlyphIconComponent }; const items: NavItem[] = [ { label: 'Home', href: '/', icon: HomeIcon }, { label: 'Settings', href: '/settings', icon: SettingsIcon }, ]; export function Nav() { return ( <nav> {items.map(({ label, href, icon: ItemIcon }) => ( <a key={href} href={href}> <ItemIcon size={18} /> {label} </a> ))} </nav> ); } export function Badge({ name }: { name: IconName }) { return <Icon name={name} variant="duotone" />; }
Imports and bundle size
Named imports from glyphfox/react are tree shaken: each icon is its own module, created with a pure annotation, and the package declares which files have side effects. Deep imports such as glyphfox/react/icons/home load a single icon module directly, which helps tools that do not tree shake and keeps development servers from touching the full index.
import { HomeIcon } from 'glyphfox/react/icons/home'; import FileIcon from 'glyphfox/react/icons/file'; // default export works too
What breaks tree shaking, what the shared runtime costs and why the build output contains one tiny chunk per icon is measured in the tree shaking guide.
Props
Every icon component, Icon, Fusion and MorphIcon take the same props. They have the same names and meaning in Vue, Svelte, Angular, the web component and toSvg, so a value copied from the studio works everywhere.
| 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 and MorphIcon also take name (any name syntax) or icon (icon data). Fusion takes icons, layout, gap, scale and offsets.
Pitfalls
Iconrenders empty at first- A name that is not registered yet is fetched with a dynamic import, so the first render (and the server render) is an empty SVG. Import the component, or call
registerIconswith the data of every name you render by string. - Next.js Server Components
- The components use hooks and the package has no
"use client"directive, so a Server Component cannot render them directly. The Next.js guide shows the one file fix and a zero JavaScript option. - Coloring with
fill - The icons are strokes on
fill="none". Passingfillfills every shape, which is rarely what you want. Usecolor(or CSScoloron a parent) for the stroke andvariant="solid"for filled shapes. - Global CSS overrides the weight
- A stylesheet rule such as
svg { stroke-width: 1.5 }wins over the SVG attribute that carries the weight axis, including its optical compensation. Scope such rules or use theweightprop. - Relative sizes skip optical compensation
- Compensation needs to know the pixel size. With
size="1em"the stroke stays at the plain weight, which can look thin at small text sizes; pass a pixel number where it matters, or a slightly higherweight. - Calling
createIconduring render createIcon(data)turns your own icon data into a component with every prop. Call it once at module level: calling it inside a component creates a new component type on every render and remounts the SVG.import *with a dynamic keyIcons[name]on a namespace import keeps every icon in the bundle. UseIconwith a name instead, which loads only what is rendered.
Questions
Can I use my own icons with the same props?
Yes. createIcon(data) from glyphfox/react takes icon data (name, body, tags, categories) and returns a component with every shared prop, including weight, the duotone and solid styles, modifiers and morphing. Draw the icon in the studio editor or convert an existing SVG with Fox-ify so it follows the same grid and stroke rules.
How do I set a default size or weight for every icon?
The React package has no provider, so the usual way is a small wrapper such as const NavIcon = (props) => <Icon size={20} weight={1.75} {...props} />. Color needs nothing: icons follow the CSS color of their parent.