{"id":13832142,"url":"https://github.com/iconfu/svg-inject","last_synced_at":"2026-04-03T00:38:31.949Z","repository":{"id":40598656,"uuid":"138699722","full_name":"iconfu/svg-inject","owner":"iconfu","description":" A tiny, intuitive, robust, caching solution for injecting SVG files inline into the DOM.","archived":false,"fork":false,"pushed_at":"2024-01-03T17:43:40.000Z","size":711,"stargazers_count":806,"open_issues_count":17,"forks_count":84,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-07-08T16:52:06.763Z","etag":null,"topics":["injection","inline-svg","svg","svg-css","svg-files","svg-icons","svg-images","svg-inject"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iconfu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-26T07:18:50.000Z","updated_at":"2025-07-02T19:10:23.000Z","dependencies_parsed_at":"2024-06-18T12:41:01.686Z","dependency_job_id":"f4225c9c-641c-4a91-8b55-9f35f11032ea","html_url":"https://github.com/iconfu/svg-inject","commit_stats":{"total_commits":395,"total_committers":4,"mean_commits":98.75,"dds":0.09873417721518984,"last_synced_commit":"064ac002930deaf96eefb95eaf953c5ef5287992"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/iconfu/svg-inject","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iconfu%2Fsvg-inject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iconfu%2Fsvg-inject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iconfu%2Fsvg-inject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iconfu%2Fsvg-inject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iconfu","download_url":"https://codeload.github.com/iconfu/svg-inject/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iconfu%2Fsvg-inject/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264495034,"owners_count":23617485,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["injection","inline-svg","svg","svg-css","svg-files","svg-icons","svg-images","svg-inject"],"created_at":"2024-08-04T10:01:52.546Z","updated_at":"2026-04-03T00:38:31.943Z","avatar_url":"https://github.com/iconfu.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","TypeScript"],"sub_categories":[],"readme":"[![npm version](https://img.shields.io/npm/v/@iconfu/svg-inject)](https://www.npmjs.com/package/@iconfu/svg-inject) [![CI](https://github.com/iconfu/svg-inject/actions/workflows/ci.yml/badge.svg)](https://github.com/iconfu/svg-inject/actions/workflows/ci.yml) [![bundle size](https://img.shields.io/bundlephobia/minzip/@iconfu/svg-inject)](https://bundlephobia.com/package/@iconfu/svg-inject)\n\n# SVGInject\n\n**Style your SVGs with CSS. No build step. No framework lock-in. ~3.5 KB gzipped.**\n\nSVGInject replaces `\u003cimg\u003e` elements with inline `\u003csvg\u003e` 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.\n\n![SVG Injection](https://github.com/iconfu/svg-inject/raw/master/resources/svg-injection.png)\n\n### Just add `onload=\"SVGInject(this)\"` to your `\u003cimg\u003e` tags.\n\n\u003e **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](#migrating-from-v1).\n\n\n## Quick start\n\n**Vanilla** - [download](https://unpkg.com/@iconfu/svg-inject@2/dist/svg-inject.min.js) or copy the file:\n```html\n\u003cscript src=\"svg-inject.min.js\"\u003e\u003c/script\u003e\n```\n\n**npm** - versioned, update with `npm update`:\n```bash\nnpm install @iconfu/svg-inject\n```\n```html\n\u003cscript src=\"node_modules/@iconfu/svg-inject/dist/svg-inject.min.js\"\u003e\u003c/script\u003e\n```\n\n**Bundler** - import and expose globally:\n```js\nimport { SVGInject } from '@iconfu/svg-inject';\nwindow.SVGInject = SVGInject;\n```\n\n**Then in your HTML:**\n```html\n\u003cimg src=\"icon.svg\" onload=\"SVGInject(this)\" /\u003e\n\u003c!-- The SVG gets injected and is styleable! --\u003e\n```\n\n**Without `onload`** - inject from JavaScript instead (better for strict [CSP](#security)):\n```js\ndocument.addEventListener('DOMContentLoaded', () =\u003e {\n  SVGInject(document.querySelectorAll('img.injectable'));\n});\n```\n\n**React, Vue, or Svelte?** See [Frameworks](#frameworks).\n\n\n## Who is it for\n\nSVGInject works best when you don't have a build step - or don't want one for your SVGs:\n\n- **WordPress, CMS, static sites** - add a `\u003cscript\u003e` tag, done\n- **Server-rendered pages** - PHP, Rails, Django, any backend template\n- **Dynamic / third-party content** - HTML injected at runtime, CMS editors, widgets\n- **Prototyping** - style SVGs with CSS without setting up tooling\n- **Multi-framework projects** - one solution across jQuery, React, vanilla, whatever\n\nSVGInject is a runtime library. It loads and injects SVGs in the browser. No build step, no bundler, no Node.js required.\n\n## Frameworks\n\nSVGInject is simpler and lighter than framework-specific packages for styling static SVGs with CSS. We don't ship framework packages - SVGInject is one function, here's how to wire it up.\n\nIf you need loading states, error boundaries, or dynamic `src` changes, look at tools like [react-inlinesvg](https://github.com/gilbarbara/react-inlinesvg) - or build your own on top of SVGInject's [API](#api).\n\n**React** - handler:\n```jsx\nimport { SVGInject } from '@iconfu/svg-inject';\nconst svgInject = (e) =\u003e SVGInject(e.currentTarget);\n\n\u003cimg src=\"icon.svg\" onLoad={svgInject} /\u003e\n```\n\n**Vue** - custom directive:\n```js\n// main.js\nimport { SVGInject } from '@iconfu/svg-inject';\napp.directive('svg-inject', {\n  mounted(el) { el.onload = () =\u003e SVGInject(el); }\n});\n```\n```vue\n\u003cimg src=\"icon.svg\" v-svg-inject /\u003e\n```\n\n**Svelte** - action:\n```svelte\n\u003cscript\u003e\n  import { SVGInject } from '@iconfu/svg-inject';\n  function svgInject(node) { node.onload = () =\u003e SVGInject(node); }\n\u003c/script\u003e\n\n\u003cimg src=\"icon.svg\" use:svgInject /\u003e\n```\n\n\n## Features\n\n### Tiny and dependency-free\n\n~3.5 KB gzipped. Zero runtime dependencies. Tree-shakeable ESM. Ships with full TypeScript definitions.\n\n### Accessible by default\n\nSVGInject automatically sets the right ARIA attributes based on your `\u003cimg\u003e`:\n\n- **`alt=\"descriptive text\"`** - sets `role=\"img\"` and `aria-label` on the SVG\n- **`alt=\"\"`** (decorative) - sets `role=\"none\"` and `aria-hidden=\"true\"`\n- **`title` attribute** - becomes a `\u003ctitle\u003e` child element inside the SVG\n- **ARIA ID references** (`aria-labelledby`, `aria-describedby`, etc.) are updated when IDs are made unique\n\n```html\n\u003c!-- Meaningful icon --\u003e\n\u003cimg src=\"chart.svg\" alt=\"Sales by quarter\" onload=\"SVGInject(this)\" /\u003e\n\u003c!-- Becomes: \u003csvg role=\"img\" aria-label=\"Sales by quarter\"\u003e ... \u003c/svg\u003e --\u003e\n\n\u003c!-- Decorative divider --\u003e\n\u003cimg src=\"divider.svg\" alt=\"\" onload=\"SVGInject(this)\" /\u003e\n\u003c!-- Becomes: \u003csvg role=\"none\" aria-hidden=\"true\"\u003e ... \u003c/svg\u003e --\u003e\n```\n\n### ID conflict prevention\n\nWhen 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.\n\n### Smart caching\n\nEach 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.\n\n### Built-in sanitization\n\nSVGInject can strip `\u003cscript\u003e` elements, `\u003cforeignObject\u003e`, `on*` event handler attributes, and `javascript:`/`data:` URIs before injection. Enable with `sanitize: true`. See [Security](#security) for details.\n\n### SSR-safe\n\nSafe 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.\n\n### Attribute handling\n\nAll attributes are copied from `\u003cimg\u003e` to `\u003csvg\u003e` with these rules:\n\n- **Excluded**: `src`, `alt`, `onload`, `onerror`\n- **`title`** becomes a `\u003ctitle\u003e` child element\n- **`alt`** becomes `aria-label` (or triggers decorative mode if empty)\n- **`style`** is merged with the SVG's existing inline style (img values win on conflicts)\n- **Case-sensitive SVG attributes** like `viewBox` and `preserveAspectRatio` are correctly mapped from their lowercased HTML form\n\nSet `copyAttributes: false` to disable and handle it yourself in `beforeInject`.\n\n\n## API\n\n```ts\nSVGInject(img, options?): Promise\u003cvoid\u003e\n```\nInjects the SVG for one or many `\u003cimg\u003e` elements. Accepts a single element, an array, or a `NodeList`. Returns a Promise that resolves when all injections complete.\n\n```ts\nSVGInject.setOptions(options): void\n```\nSets global default options for all subsequent injections.\n\n```ts\nSVGInject.create(name, options?): SVGInjectFunction\n```\nCreates 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.\n\n```ts\nSVGInject.err(img, fallbackSrc?): void\n```\nError handler for `onerror` on `\u003cimg\u003e` elements. Optionally sets a fallback `src`.\n\n### Options\n\n| Property | Type | Default | Description |\n|----------|------|---------|-------------|\n| `useCache` | `boolean` | `true` | Cache SVG content per URL for the page lifetime |\n| `copyAttributes` | `boolean` | `true` | Copy attributes from `\u003cimg\u003e` to `\u003csvg\u003e` |\n| `makeIdsUnique` | `boolean` | `true` | Append `--inject-N` suffix to all IDs to prevent collisions |\n| `sanitize` | `boolean` | `false` | Strip `\u003cscript\u003e`, `\u003cforeignObject\u003e`, event handlers, and dangerous URIs before injection |\n| `injectStyleTag` | `boolean` | `true` | Inject a `\u003cstyle\u003e` tag to hide images before injection, preventing unstyled image flash. Set to `false` if you have a strict CSP |\n| `beforeLoad` | `(img) =\u003e string \\| void` | | Hook before loading. Return a string to override the URL |\n| `afterLoad` | `(svg, svgString) =\u003e string \\| SVGSVGElement \\| void` | | Hook after loading. Modify the SVG or return a new one. Called once per URL when caching is enabled |\n| `beforeInject` | `(img, svg) =\u003e Element \\| void` | | Hook before injection. Return an element to inject instead |\n| `afterInject` | `(img, svg) =\u003e void` | | Hook after injection |\n| `onAllFinish` | `() =\u003e void` | | Called when all elements in a batch are done |\n| `onFail` | `(img, status) =\u003e void` | | Called on failure. Status is `'LOAD_FAIL'`, `'SVG_INVALID'`, or `'SVG_NOT_SUPPORTED'` |\n\n\n## Unstyled image flash\n\nWhen using `onload`, the browser may briefly show the raw `\u003cimg\u003e` before injection replaces it. SVGInject prevents this by default - it injects a CSS rule that hides injectable images until injection is complete.\n\nThis 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:\n\n```js\nSVGInject.setOptions({ injectStyleTag: false });\n```\n\n```css\nimg[onload^=\"SVGInject(\"] { visibility: hidden; }\n```\n\n\n## Error handling\n\n```html\n\u003cimg src=\"icon.svg\"\n     onload=\"SVGInject(this)\"\n     onerror=\"SVGInject.err(this, 'fallback.png')\" /\u003e\n```\n\nOr use the `onFail` hook:\n\n```js\nSVGInject.setOptions({\n  onFail(img, status) {\n    console.error('Injection failed:', status); // 'LOAD_FAIL', 'SVG_INVALID', or 'SVG_NOT_SUPPORTED'\n  }\n});\n```\n\n\n## Security\n\nSVG files can contain scripts. SVGInject includes built-in protections you can opt into:\n\n- **Built-in sanitization.** Enable with `SVGInject.setOptions({ sanitize: true })` or per call with `SVGInject(img, { sanitize: true })`. This strips `\u003cscript\u003e`, `\u003cforeignObject\u003e`, `on*` event handlers, and `javascript:`/`data:` URIs before injection, catching the most common XSS vectors.\n- **For untrusted SVGs** (user uploads, third-party URLs), consider adding [DOMPurify](https://github.com/cure53/DOMPurify) in the `afterLoad` hook for comprehensive protection.\n- **Same-origin policy applies.** SVGs are loaded with `fetch()`. Cross-origin SVGs require [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) on the server.\n- **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](#quick-start)).\n\n\n## Migrating from v1\n\nv2 is API-compatible with v1. Breaking changes:\n\n| Change | Migration |\n|--------|-----------|\n| IE9-11 no longer supported | Stay on v1.x for IE |\n| Decorative images handled correctly | `alt=\"\"` now sets `role=\"none\"` + `aria-hidden=\"true\"` |\n| `alt` converted to `aria-label` | Accessibility improvement |\n| `style` merged instead of overwritten | Better behavior when both img and SVG have inline styles |\n\n\n## Browser support\n\nChrome, Firefox, Safari, Edge - all modern versions.\n\n\n## License\n\n[MIT](https://github.com/iconfu/svg-inject/blob/master/LICENSE) - Developed and maintained by [INCORS](https://www.incors.com).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficonfu%2Fsvg-inject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ficonfu%2Fsvg-inject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficonfu%2Fsvg-inject/lists"}