Use the 3,504 icons in 4 styles with your framework, from a CDN, or as plain SVG.
Healing Icons has 3,504 icons in 4 styles. Pick the package for your stack: every package has the same icons, names and options.
Icon names are English kebab-case, like arrow-left. Components use PascalCase with an Icon suffix: ArrowLeftIcon (names that start with a digit get a prefix instead: Icon2fa). Find names with the search on the icon library, or press Ctrl K there.
Every package exposes the styles as entry points: …/outline (the default), …/outline-duo, …/fill and …/glyph-duo. Icons are drawn on an 18 × 18 grid; outline icons use a 1.5 stroke.
Single-color icons use currentColor, so they follow the text color unless you set color. Duo styles have a second layer you can recolor with secondaryColor.
import { HeartIcon } from "@healing-icons/react"; // outline (default)
import { HeartIcon as HeartFill } from "@healing-icons/react/fill";
export function Like({ liked }) {
return liked ? <HeartFill color="tomato" title="Liked" /> : <HeartIcon size={20} />;
}Each icon is its own module, so bundlers only ship the icons you import. You can also import a single file: import HeartIcon from "@healing-icons/react/outline/HeartIcon". Components forward ref and pass other props (className, onClick, aria-*) to the <svg>. They work in Server Components.
<script setup>
import { HeartIcon } from "@healing-icons/vue";
import { HeartIcon as HeartFill } from "@healing-icons/vue/fill";
defineProps(["liked"]);
</script>
<template>
<HeartFill v-if="liked" color="tomato" title="Liked" />
<HeartIcon v-else :size="20" :stroke-width="2" />
</template>Vue 3.3 or newer. Props can be written in kebab-case (secondary-color, stroke-width). class, style and listeners such as @click fall through to the <svg>.
<script>
import HeartIcon from "@healing-icons/svelte/outline/HeartIcon";
import HeartFill from "@healing-icons/svelte/fill/HeartIcon";
let { liked } = $props();
</script>
{#if liked}
<HeartFill color="tomato" title="Liked" />
{:else}
<HeartIcon size={20} />
{/if}Svelte 5. Single-icon imports (…/<style>/<Component>) are the fastest, in dev and in builds. Named imports such as import { HeartIcon } from "@healing-icons/svelte/fill" also work and are tree-shaken, but make the Vite dev server process the whole style once.
import { Component, input } from "@angular/core";
import { HealingIcon, HeartIcon } from "@healing-icons/angular";
import { HeartIcon as HeartFill } from "@healing-icons/angular/fill";
@Component({
selector: "app-like",
imports: [HealingIcon],
template: `
@if (liked()) {
<healing-icon [icon]="HeartFill" color="tomato" title="Liked" />
} @else {
<healing-icon [icon]="HeartIcon" [size]="20" />
}
`,
})
export class LikeComponent {
readonly liked = input(false);
protected readonly HeartIcon = HeartIcon;
protected readonly HeartFill = HeartFill;
}Angular 17 or newer. <healing-icon> is a standalone component; icons are plain data, so only the ones you import end up in the build. It supports server rendering (hydration skips the icon and re-creates it on the client).
With Expo, install the renderer with npx expo install react-native-svg.
import HeartIcon from "@healing-icons/react-native/outline/HeartIcon";
import HeartFill from "@healing-icons/react-native/fill/HeartIcon";
export function Like({ liked }: { liked: boolean }) {
return liked ? <HeartFill color="tomato" title="Liked" /> : <HeartIcon size={20} color="#14201b" />;
}React Native has no inherited text color, so color defaults to black. Metro doesn't tree-shake, so prefer single-icon imports as above. title becomes the accessibility label; other react-native-svg props (style, onPress, testID) go to the <Svg>.
<healing-icon> works in plain HTML and in any framework. The quickest start is a CDN: the lazy build loads each icon the first time it's used.
<script type="module" src="https://cdn.jsdelivr.net/npm/@healing-icons/elements/dist/lazy.js"></script> <healing-icon name="heart"></healing-icon> <healing-icon name="heart" variant="fill" size="32" color="tomato" label="Liked"></healing-icon>
With a bundler, install the package and import the icons you use:
import "@healing-icons/elements"; import "@healing-icons/elements/outline/heart"; // registers <healing-icon name="heart"> import "@healing-icons/elements/fill/star"; // registers <healing-icon name="star" variant="fill">
Attributes mirror the component props in kebab-case (secondary-color, stroke-width, absolute-stroke-width); use label for an accessible name. The icon renders in a shadow root; style it with healing-icon::part(svg). In Vue, mark it as a custom element (compilerOptions.isCustomElement); in Angular, add CUSTOM_ELEMENTS_SCHEMA.
The healing-icons package has the optimized SVG files, at healing-icons/svg/<style>/<name>.svg.
import heartUrl from "healing-icons/svg/outline/heart.svg";
<img src={heartUrl} width="24" height="24" alt="" />Every file is also on jsDelivr and unpkg. Pin a version in production. An <img> can't follow the text color; use the icon as a CSS mask for that:
<img src="https://cdn.jsdelivr.net/npm/healing-icons@0.7/svg/outline/heart.svg" width="24" height="24" alt="">
<style>
.icon-heart {
width: 24px; height: 24px;
background: currentColor;
mask: url(https://cdn.jsdelivr.net/npm/healing-icons@0.7/svg/outline/heart.svg) center / contain no-repeat;
}
</style>Sprites put many icons in one file. Build one with just the icons you need, then reference them with <use>:
npx healing-icons sprite heart star arrow-left --style outline --out public/icons.svg
<svg width="24" height="24"><use href="/icons.svg#heart"/></svg>
On the icon library, Select icons to download them as a ZIP of SVG files or as a sprite.
Search icons with previews and insert them as code for the file you're in (Ctrl Alt I), with the import added. Hover an icon name to see it.
code --install-extension hoanghuilinh.healing-icons-vscode
The same options in every package. React Native accepts numbers only for size; the web component takes them as kebab-case attributes.
| Prop | Default | Description |
|---|---|---|
| size | 24 | Width and height (number or CSS length) |
| color | currentColor | Icon color; single-color icons follow the text color |
| secondaryColor | color | Color of the second layer of duo icons |
| strokeWidth | 1.5 | Stroke width of outline icons, in grid units (18 per icon) |
| absoluteStrokeWidth | false | Treat strokeWidth as pixels, the same at every size |
| title | Accessible name; without it the icon is hidden from screen readers |
Every package exports an IconName type (a union of all names) and IconStyle. When the icon is only known at runtime (CMS, config), DynamicIcon loads it on demand:
import { DynamicIcon, type IconName } from "@healing-icons/react/dynamic";
function MenuItem({ icon, label }: { icon: IconName; label: string }) {
return (
<li>
<DynamicIcon name={icon} variant="fill" fallback={<span className="size-6" />} />
{label}
</li>
);
}Vue and Svelte: DynamicIcon from …/dynamic (with a fallback slot / snippet). Angular: <healing-dynamic-icon [name]="…"> from @healing-icons/angular/dynamic. Web component: the lazy build always loads by name.
Prefer static imports when you can. With DynamicIcon, every icon becomes a lazy chunk of your build (React Native's Metro bundles all of them), and showing one costs an extra request.
Icons are decorative by default: they're hidden from screen readers (aria-hidden). When an icon carries meaning on its own, give it a title (label on the web component) and it becomes an image with that name.
// Icon-only button: name the button, keep the icon decorative. <button aria-label="Delete"><TrashIcon /></button> // Icon that stands alone <WarningIcon title="Warning" color="#b45309" />