An SVG logo being recolored on a monitor with a gray-blue British shorthair cat resting nearby.

react-inlinesvg: Give Your SVGs CSS Superpowers

The Gray Cat
The Gray Cat
0 views

There are a surprising number of ways to put an SVG on a web page, and almost all of them come with an annoying catch. Drop an .svg file into an <img> tag and it renders perfectly, but you have zero control over its internals: you cannot change the fill with CSS, cannot animate a path, cannot target a node. Use it as a CSS background and you hit the same wall. Hand-paste the raw <svg> markup straight into your JSX and you get full styling control, but now your component is bloated, the icon is not reusable, and nothing is cached.

react-inlinesvg threads this needle. It is a tiny React component that takes a src (a URL, a local path, a data URI, or even a raw SVG string), fetches it if needed, and injects the result inline into the DOM as a real <svg> element. Because the markup lands directly in the document, you can style it from the parent with fill: currentColor, animate its paths, target internal nodes, and treat it like any other piece of DOM. You get the styleability of inline SVG with the ergonomics of a simple src prop, plus runtime caching for the SVGs you do not control at build time. It is a staple of the ecosystem, pulling in over 740,000 weekly downloads, and at roughly 7.8 KB gzipped it earns its keep.

What Makes It Worth Reaching For

A few things set this component apart from the half-dozen other ways to render an SVG:

  • Real inline SVG, not a wrapper. The output is an actual <svg> node, so every CSS trick you know works on it and its children.
  • Flexible source. src accepts a remote URL, a local path, a base64 or URL-encoded data URI, or a literal SVG string.
  • Runtime fetching and caching. Requests are cached in memory by default, with optional persistent browser storage through a provider.
  • Loaders and error fallbacks. Show a placeholder while loading and graceful fallback content if a fetch fails.
  • A preProcessor hook. Rewrite the SVG markup before it renders, which is how you recolor icons on the fly.
  • ID de-duplication. Render the same gradient-heavy SVG many times without ID collisions.
  • SSR-safe and modern. Works with server-side rendering and supports React 16.8 through 19.

Getting It Into Your Project

The package has a single tiny runtime dependency, so installation is quick.

npm install react-inlinesvg
yarn add react-inlinesvg

That is everything you need for the basic component. The optional persistent cache lives in a separate entry point that ships in the same package, so there is nothing extra to install for it.

Your First Inlined SVG

The default export is the component itself. Import it, point src at an SVG, and you are done. Any extra props you pass that the component does not recognize are forwarded straight onto the rendered <svg> element.

import SVG from 'react-inlinesvg';

export default function Logo() {
  return (
    <SVG
      src="https://cdn.svglogos.dev/logos/react.svg"
      width={128}
      height="auto"
      title="React"
    />
  );
}

Here width, height, and title all flow through to the real SVG node. The title prop is special-cased: it sets the SVG's accessible <title> element, and passing null removes it entirely. The same component happily takes a local path or a raw string instead of a URL.

const star = '<svg viewBox="0 0 24 24"><path d="M12 2l3 7h7l-6 4 2 7-6-4-6 4 2-7-6-4h7z"/></svg>';

<SVG src="/icons/menu.svg" className="icon" />
<SVG src={star} className="icon" />

The Recoloring Trick Everyone Wants

This is the feature that sells the library. Because the SVG is genuinely inline, you can make it inherit your text color with one line of CSS, but only if the SVG's paths use currentColor rather than a hardcoded fill. Most icon files ship with a baked-in fill, so the preProcessor prop lets you rewrite the markup before it renders.

import SVG from 'react-inlinesvg';

export function ColorableIcon({ src, color }: { src: string; color: string }) {
  return (
    <SVG
      src={src}
      preProcessor={(code) => code.replace(/fill=".*?"/g, 'fill="currentColor"')}
      style={{ color }}
      width={24}
      height={24}
    />
  );
}

Now the same icon file can be crimson in one place and muted gray in another, driven entirely by CSS color. You can hover-recolor it, transition it, or theme it for dark mode without touching the original file. The preProcessor receives the raw SVG string and returns a transformed one, so it is a general-purpose escape hatch: strip hardcoded widths, inject a class, or normalize sloppy markup from a CMS.

Loaders and Graceful Failure

Because remote SVGs travel over the network, you will sometimes want to show something while one loads and something else if it never arrives. react-inlinesvg covers both. The loader prop renders during the fetch, onError fires on failure, and any children you pass act as fallback content when the load fails or the browser cannot render inline SVG.

import SVG from 'react-inlinesvg';

export function RemoteIcon({ src }: { src: string }) {
  return (
    <SVG
      src={src}
      loader={<span className="spinner" aria-hidden />}
      onLoad={(loadedSrc, isCached) => {
        console.log(`Loaded ${loadedSrc} (from cache: ${isCached})`);
      }}
      onError={(error) => {
        console.warn('SVG failed to load:', error.message);
      }}
    >
      <img src="/icons/fallback.png" alt="Icon unavailable" />
    </SVG>
  );
}

Note the isCached flag handed to onLoad: it tells you whether the SVG came from the in-memory cache or a fresh request, which is handy for instrumentation. One small gotcha worth knowing is that remote SVGs must serve proper CORS headers; if you load icons from a third-party CDN and they silently fail, a missing Access-Control-Allow-Origin header is the usual culprit.

Rendering the Same SVG Many Times

SVGs that use gradients, masks, or filters reference internal IDs like url(#grad1). Render that same SVG five times on a page and you now have five elements all claiming id="grad1", and the browser will resolve every reference to the first one it finds, producing broken or identical-looking icons. The uniquifyIDs prop solves this by rewriting every ID to a unique value per instance.

import SVG from 'react-inlinesvg';

export function BadgeList({ count }: { count: number }) {
  return (
    <div className="badges">
      {Array.from({ length: count }, (_, i) => (
        <SVG key={i} src="/icons/gradient-badge.svg" uniquifyIDs />
      ))}
    </div>
  );
}

If your page also has an HTML <base> tag, pass baseURL so the rewritten url() references resolve correctly. And as of recent versions, ID rewriting also reaches into <style> blocks inside the SVG, so CSS-driven gradients stay intact too.

Persistent Caching Across Page Loads

By default, cacheRequests is true, so each unique src is fetched once and kept in memory for the lifetime of the page. That is enough for most apps. But if you want SVGs to survive navigations and reloads, wrap your app in the CacheProvider from the dedicated provider entry point. It persists fetched SVGs in browser storage, so a returning visitor skips the network round-trip entirely.

import { createRoot } from 'react-dom/client';
import CacheProvider from 'react-inlinesvg/provider';
import App from './App';

createRoot(document.getElementById('root')!).render(
  <CacheProvider name="app-svg-cache">
    <App />
  </CacheProvider>,
);

The optional name prop customizes the storage key, which is useful if you run multiple apps on the same origin and want their caches to stay separate.

Driving the Lifecycle Yourself

For cases where the <SVG> component does not quite fit your wrapper or loading UI, recent versions expose a useInlineSVG hook that surfaces the same loading lifecycle directly. You get the parsed content and status and render whatever you like around it, which is handy when you are building a custom icon system with bespoke skeletons or animation wrappers.

import { useInlineSVG } from 'react-inlinesvg';

export function CustomIcon({ src }: { src: string }) {
  const { content, isLoading, hasError } = useInlineSVG({ src });

  if (isLoading) return <div className="icon-skeleton" />;
  if (hasError) return <span aria-label="icon failed">⚠️</span>;

  return <span className="icon-shell">{content}</span>;
}

This keeps you in control of the DOM structure while still leaning on the library's fetching, parsing, and caching machinery.

Picking the Right Tool

It is worth being honest about where react-inlinesvg fits. If your icon set is known at build time and lives in your repo, a build-time transform like SVGR turns each SVG into a tree-shakeable React component with no runtime fetch at all, and that is genuinely the better choice for static icon libraries. react-inlinesvg shines in the other half of the world: SVGs you do not control at build time. Think CDN-hosted logos, user-uploaded artwork, icons that come back from a CMS or an API, or anything you need to recolor and restyle at runtime. Among runtime SVG loaders it is the most popular and best-maintained option, with a clean issue tracker and steady releases focused on cache correctness and stability.

Wrapping Up

react-inlinesvg solves a small but genuinely irritating problem with quiet competence. Point it at an SVG, get a real inline <svg> you can style with CSS, and lean on preProcessor for the recoloring trick, loader and children for graceful loading, uniquifyIDs for repeated instances, and the CacheProvider for persistence. It is tiny, SSR-safe, works across React 16.8 through 19, and has earned its 740,000 weekly downloads by staying focused and reliable. When you need styleable SVGs that you load at runtime, this is the component to reach for.