npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@iconfu/svg-inject

v2.0.1

Published

A tiny, intuitive, robust, caching solution for injecting SVG files inline into the DOM.

Readme

npm version CI bundle size

SVGInject

Style your SVGs with CSS. No build step. No framework lock-in. ~3.5 KB gzipped.

SVGInject replaces <img> elements with inline <svg> so you can target every path, circle, and group with CSS - colors, animations, hover effects, dark mode, all of it. One line of code, works everywhere.

SVG Injection

Just add onload="SVGInject(this)" to your <img> tags.

Using v1? v2 is a drop-in upgrade - same API, no code changes needed. You get bug fixes, better accessibility, and a full test suite. Only downside: no more IE support. See what changed.

Quick start

Vanilla - download or copy the file:

<script src="svg-inject.min.js"></script>

npm - versioned, update with npm update:

npm install @iconfu/svg-inject
<script src="node_modules/@iconfu/svg-inject/dist/svg-inject.min.js"></script>

Bundler - import and expose globally:

import { SVGInject } from '@iconfu/svg-inject';
window.SVGInject = SVGInject;

Then in your HTML:

<img src="icon.svg" onload="SVGInject(this)" />
<!-- The SVG gets injected and is styleable! -->

Without onload - inject from JavaScript instead (better for strict CSP):

document.addEventListener('DOMContentLoaded', () => {
  SVGInject(document.querySelectorAll('img.injectable'));
});

React, Vue, or Svelte? See Frameworks.

Who is it for

SVGInject works best when you don't have a build step - or don't want one for your SVGs:

  • WordPress, CMS, static sites - add a <script> tag, done
  • Server-rendered pages - PHP, Rails, Django, any backend template
  • Dynamic / third-party content - HTML injected at runtime, CMS editors, widgets
  • Prototyping - style SVGs with CSS without setting up tooling
  • Multi-framework projects - one solution across jQuery, React, vanilla, whatever

SVGInject is a runtime library. It loads and injects SVGs in the browser. No build step, no bundler, no Node.js required.

What about React, Vue, Svelte? SVGInject works great in frameworks too - see Frameworks for idiomatic setup patterns. For styling static SVGs with CSS, it's simpler and lighter than framework-specific packages. If you need loading states, error boundaries, or dynamic src changes, look at tools like react-inlinesvg.

Frameworks

We don't ship framework-specific packages. SVGInject is one function - here's how to wire it up:

React - handler:

import { SVGInject } from '@iconfu/svg-inject';
const svgInject = (e) => SVGInject(e.currentTarget);

<img src="icon.svg" onLoad={svgInject} />

Vue - custom directive:

// main.js
import { SVGInject } from '@iconfu/svg-inject';
app.directive('svg-inject', {
  mounted(el) { el.onload = () => SVGInject(el); }
});
<img src="icon.svg" v-svg-inject />

Svelte - action:

<script>
  import { SVGInject } from '@iconfu/svg-inject';
  function svgInject(node) { node.onload = () => SVGInject(node); }
</script>

<img src="icon.svg" use:svgInject />

Features

Tiny and dependency-free

~3.5 KB gzipped. Zero runtime dependencies. Tree-shakeable ESM. Ships with full TypeScript definitions.

Accessible by default

SVGInject automatically sets the right ARIA attributes based on your <img>:

  • alt="descriptive text" - sets role="img" and aria-label on the SVG
  • alt="" (decorative) - sets role="none" and aria-hidden="true"
  • title attribute - becomes a <title> child element inside the SVG
  • ARIA ID references (aria-labelledby, aria-describedby, etc.) are updated when IDs are made unique
<!-- Meaningful icon -->
<img src="chart.svg" alt="Sales by quarter" onload="SVGInject(this)" />
<!-- Becomes: <svg role="img" aria-label="Sales by quarter"> ... </svg> -->

<!-- Decorative divider -->
<img src="divider.svg" alt="" onload="SVGInject(this)" />
<!-- Becomes: <svg role="none" aria-hidden="true"> ... </svg> -->

ID conflict prevention

When multiple SVGs on the same page share IDs (common with gradient or clipPath definitions), SVGInject automatically makes all IDs unique by appending --inject-N suffixes. References in url(), href, xlink:href, and ARIA attributes are updated to match.

Smart caching

Each SVG URL is fetched once and cached for the page lifetime. Subsequent injections of the same URL reuse the cached SVG string but parse a fresh DOM element, so ID uniquification and sanitization are always applied.

Built-in sanitization

SVGInject can strip <script> elements, <foreignObject>, on* event handler attributes, and javascript:/data: URIs before injection. Enable with sanitize: true. See Security for details.

SSR-safe

Safe to import in Node.js, Next.js, Nuxt, SvelteKit, and any server-side environment. All DOM access is deferred to function call time - no top-level window or document references.

Attribute handling

All attributes are copied from <img> to <svg> with these rules:

  • Excluded: src, alt, onload, onerror
  • title becomes a <title> child element
  • alt becomes aria-label (or triggers decorative mode if empty)
  • style is merged with the SVG's existing inline style (img values win on conflicts)
  • Case-sensitive SVG attributes like viewBox and preserveAspectRatio are correctly mapped from their lowercased HTML form

Set copyAttributes: false to disable and handle it yourself in beforeInject.

API

SVGInject(img, options?): Promise<void>

Injects the SVG for one or many <img> elements. Accepts a single element, an array, or a NodeList. Returns a Promise that resolves when all injections complete.

SVGInject.setOptions(options): void

Sets global default options for all subsequent injections.

SVGInject.create(name, options?): SVGInjectFunction

Creates an independent instance with its own cache and options. The name parameter sets the global variable name (used for onload attribute binding) and the flash-prevention CSS selector.

SVGInject.err(img, fallbackSrc?): void

Error handler for onerror on <img> elements. Optionally sets a fallback src.

Options

| Property | Type | Default | Description | |----------|------|---------|-------------| | useCache | boolean | true | Cache SVG content per URL for the page lifetime | | copyAttributes | boolean | true | Copy attributes from <img> to <svg> | | makeIdsUnique | boolean | true | Append --inject-N suffix to all IDs to prevent collisions | | sanitize | boolean | false | Strip <script>, <foreignObject>, event handlers, and dangerous URIs before injection | | injectStyleTag | boolean | true | Inject a <style> tag to hide images before injection, preventing unstyled image flash. Set to false if you have a strict CSP | | beforeLoad | (img) => string \| void | | Hook before loading. Return a string to override the URL | | afterLoad | (svg, svgString) => string \| SVGSVGElement \| void | | Hook after loading. Modify the SVG or return a new one. Called once per URL when caching is enabled | | beforeInject | (img, svg) => Element \| void | | Hook before injection. Return an element to inject instead | | afterInject | (img, svg) => void | | Hook after injection | | onAllFinish | () => void | | Called when all elements in a batch are done | | onFail | (img, status) => void | | Called on failure. Status is 'LOAD_FAIL', 'SVG_INVALID', or 'SVG_NOT_SUPPORTED' |

Unstyled image flash

When using onload, the browser may briefly show the raw <img> before injection replaces it. SVGInject prevents this by default - it injects a CSS rule that hides injectable images until injection is complete.

This requires style-src 'unsafe-inline' in your Content Security Policy. If you have a strict CSP, disable it and add the rule to your own stylesheet instead:

SVGInject.setOptions({ injectStyleTag: false });
img[onload^="SVGInject("] { visibility: hidden; }

Error handling

<img src="icon.svg"
     onload="SVGInject(this)"
     onerror="SVGInject.err(this, 'fallback.png')" />

Or use the onFail hook:

SVGInject.setOptions({
  onFail(img, status) {
    console.error('Injection failed:', status); // 'LOAD_FAIL', 'SVG_INVALID', or 'SVG_NOT_SUPPORTED'
  }
});

Security

SVG files can contain scripts. SVGInject includes built-in protections you can opt into:

  • Built-in sanitization. Enable with SVGInject.setOptions({ sanitize: true }) or per call with SVGInject(img, { sanitize: true }). This strips <script>, <foreignObject>, on* event handlers, and javascript:/data: URIs before injection, catching the most common XSS vectors.
  • For untrusted SVGs (user uploads, third-party URLs), consider adding DOMPurify in the afterLoad hook for comprehensive protection.
  • Same-origin policy applies. SVGs are loaded with fetch(). Cross-origin SVGs require CORS headers on the server.
  • CSP note. The onload="..." attribute requires script-src 'unsafe-inline' (or a nonce/hash). If you use a strict CSP, call SVGInject from JavaScript instead (see Quick start).

Migrating from v1

v2 is API-compatible with v1. Breaking changes:

| Change | Migration | |--------|-----------| | IE9-11 no longer supported | Stay on v1.x for IE | | Decorative images handled correctly | alt="" now sets role="none" + aria-hidden="true" | | alt converted to aria-label | Accessibility improvement | | style merged instead of overwritten | Better behavior when both img and SVG have inline styles |

Browser support

Chrome, Firefox, Safari, Edge - all modern versions.

License

MIT - Developed and maintained by INCORS.