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

@svgin/element

v0.1.5

Published

Framework-agnostic <svg-in> custom element: securely fetch and inline SVGs from URLs, sanitized by default.

Readme

@svgin/element

Framework-agnostic <svg-in> custom element: securely fetch and inline SVGs from URLs, sanitized by default.

Built on vanilla native Custom Elements (class SvgIn extends HTMLElement), no Lit, no Stencil, for zero framework dependency and the smallest possible bundle, wrapping svgin-core's fetch/sanitize/cache logic - the same internals @svgin/react's <SvgIn /> is built on. The default sanitizer still needs DOMPurify (an optional peer dependency, same as @svgin/react), unless you pass your own sanitizeFn or disableSanitization.

Published to npm as @svgin/element, demoed live in apps/tryit at /element.

Using React?

@svgin/react's <SvgIn /> is the same sanitize-by-default SVG loading as a React component, with client and server variants, <SvgInProvider> shared defaults, Suspense support, and more - built on the same svgin-core internals as this package. Published to npm as @svgin/react.

Usage

<script type="module">
    import '@svgin/element'; // registers <svg-in> as a side effect
</script>

<svg-in src="/icons/logo.svg" width="24" height="24" fill="currentColor"></svg-in>

Using from a CDN, no build tool

@svgin/element (and svg-in itself) has no framework dependency, but the default sanitizer still needs DOMPurify - loaded lazily via a bare import('dompurify'), which only resolves in a plain browser <script type="module"> (no bundler, no npm install) if an import map tells the browser where to fetch it from:

<script type="importmap">
{
  "imports": {
    "dompurify": "https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.es.mjs"
  }
}
</script>
<script type="module" src="https://unpkg.com/@svgin/element"></script>

<svg-in src="/icons/logo.svg" width="24" height="24" fill="currentColor"></svg-in>

Any npm package is automatically mirrored on unpkg and jsDelivr - no separate CDN publish step needed. Pin exact versions (e.g. @svgin/[email protected], [email protected]) for anything beyond a quick demo. If you pass your own sanitizeFn or use disable-sanitization, DOMPurify is never imported at all and the import map can be skipped entirely.

Rendering model: light DOM, not shadow DOM

<svg-in> inlines the sanitized <svg> as a direct child of the element itself (light DOM), not inside a shadow root. This is a deliberate choice, not an oversight - it mirrors <SvgIn />'s own default in @svgin/react (<SvgInShadow /> is the separate, shadow-DOM opt-in component over there). Rendering into light DOM means ordinary page CSS can style the icon directly (svg-in svg { fill: ... }, or via the class/fill/width/height attributes forwarded straight onto the rendered <svg> - see below). A shadow-DOM rendering mode (e.g. a shadow attribute) is left for a follow-up once there's a real consumer need for it - this first implementation only ships the light-DOM behavior.

Attributes

Every DOM attribute is a string; see below for how each is read/coerced.

| Attribute | Meaning | | --- | --- | | src | URL to fetch. Ignored if svg is also present. | | svg | Raw SVG markup already in hand - sanitized and rendered directly, skipping the fetch step. Takes precedence over src. | | width / height / fill | Forwarded verbatim (as strings - no numeric coercion) to the rendered <svg>, both for the loading placeholder and the resolved result. | | class | The native attribute already applies to the <svg-in> host regardless; it's additionally forwarded onto the rendered inner <svg> (mirrors <SvgIn className={...} /> applying className to the rendered <svg>, not a wrapper). | | aria-label | Forwarded to the rendered <svg>. An explicit aria-label disables the auto-wired aria-labelledby this element otherwise sets when svg-title is present. | | svg-title / svg-description | Injects a <title>/<desc> into the rendered SVG (accessible name/description). Deliberately not named title/description: a plain title attribute is a global HTML attribute every browser already gives special meaning (a hover tooltip) to on any element - reusing that name would silently conflict with native behavior instead of cleanly mapping to SvgInProps.title. | | disable-sanitization | Boolean attribute (presence, not value - same convention as disabled/hidden). Skips DOMPurify entirely. Only use this for markup you already trust. | | loading | 'eager' (default) or 'lazy'. 'lazy' defers the fetch/sanitize until the element scrolls near the viewport, via IntersectionObserver (rootMargin: '200px', same as <SvgIn loading="lazy" />). Falls back to eager loading when IntersectionObserver isn't available, or when svg is set (there's nothing to fetch). |

attributeChangedCallback reacts to src/svg/disable-sanitization changes by restarting the whole fetch/sanitize cycle. Changing a purely presentational attribute (width/height/fill/class/ aria-label/svg-title/svg-description) re-renders the already-resolved (or still-pending) result without touching the network again.

sanitizeFn / fetchOptions: JS properties, not attributes

document.querySelector('svg-in').sanitizeFn = async (svg) => svg; // custom sanitizer
document.querySelector('svg-in').fetchOptions = { headers: { Authorization: 'Bearer ...' } };

A function or an arbitrary request-init object has no meaningful string attribute representation, so these are plain JS properties instead of attributes. Setting either while the element is connected restarts the fetch/sanitize cycle, same as changing src.

Events instead of callback props

There is no onError/onMount callback property the way <SvgIn onError={...} onMount={...} /> has one - a plain custom element has no prop channel for a callback to hang off of. Instead, <svg-in> dispatches two bubbling, composed CustomEvents:

  • svg-in-load - detail: { svg: SVGSVGElement } - once the sanitized SVG is inlined.
  • svg-in-error - detail: { error: Error } - when the fetch or sanitization fails. Nothing is rendered in this case (any previous content, including the loading placeholder, is cleared); there is no fallback attribute equivalent (an arbitrary fallback has no attribute-string representation) - listen for this event and react instead (set different attributes, swap the element, etc.).
const el = document.querySelector('svg-in');
el.addEventListener('svg-in-load', (e) => console.log('loaded', e.detail.svg));
el.addEventListener('svg-in-error', (e) => console.error('failed', e.detail.error));

Lifecycle

  • connectedCallback starts (or restarts) the load cycle. Disconnecting and reconnecting an element (e.g. moving it in the DOM) restarts the load cycle from scratch - the same semantics a React unmount+remount would have (the shared svgCache is what keeps that fast rather than a genuine network refetch).
  • disconnectedCallback releases any in-flight fetch via releaseFetchAndSanitizeSvg (the same reference-counted cache-release pattern <SvgIn />'s effect cleanup uses) and tears down any active IntersectionObserver.
  • Double-registration (customElements.define('svg-in', ...)) is guarded against, so loading this module more than once (two bundles, an HMR reload) is safe.

Bundle size

Small on purpose: a few KB gzipped, with zero runtime dependencies beyond the optional DOMPurify sanitizer.

Contributing

This package lives inside the svgin monorepo - see that repo for the build/test/release setup.