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

@acusti/vite-plugin-svg-react

v0.1.0

Published

Vite ≥8 (rolldown-native) plugin that imports SVG files as typed React components via ?react

Downloads

198

Readme

@acusti/vite-plugin-svg-react

latest version maintenance status downloads per month

A Vite plugin that turns SVG files into typed React components using SVGR:

import Icon from './icon.svg?react';

<Icon className="icon" aria-hidden />;

It was extracted from the build tooling of Outlyne, where it runs in production.

Why Vite ≥ 8 only?

This plugin requires Vite 8 and is rolldown-native, on purpose. The Vite 8 / rolldown-vite transition left no working SVGR option: vite-plugin-svgr runs its own esbuild transform to compile the JSX that SVGR emits, reintroducing esbuild into an otherwise oxc/rolldown pipeline. This plugin instead compiles the JSX with Vite 8’s exported transformWithOxc, so SVG-to-React conversion is oxc/rolldown end to end: no esbuild fallback, no version matrix, no compatibility shims for older Vite versions. If you are on Vite < 8, use vite-plugin-svgr.

Usage

npm install --save-dev @acusti/vite-plugin-svg-react
# or
yarn add --dev @acusti/vite-plugin-svg-react

Add the plugin to your vite config:

// vite.config.ts
import svgReact from '@acusti/vite-plugin-svg-react';
import { defineConfig } from 'vite';

export default defineConfig({
    plugins: [svgReact()],
});

Then import SVG files with the ?react query suffix to get a React component. The default export is a component that spreads its props onto the root <svg> element:

import Logo from './logo.svg?react';

export function Header() {
    return <Logo width={32} height={32} role="img" />;
}

Note that react isn’t a dependency or peer dependency of this package: the emitted components import react/jsx-runtime (or react/jsx-dev-runtime in dev), which your app provides.

TypeScript

The package ships a client.d.ts that types *.svg?react imports as React.FC<React.SVGProps<SVGSVGElement>>. Wire it up either via the types field in your tsconfig:

{
    "compilerOptions": {
        "types": ["@acusti/vite-plugin-svg-react/client"]
    }
}

Or via a triple-slash directive in a .d.ts file that’s included in your project (e.g. src/vite-env.d.ts):

/// <reference types="@acusti/vite-plugin-svg-react/client" />

Options

The plugin takes an optional options object with a single property: svgrOptions, which is passed to @svgr/core’s transform and shallow-merged over the defaults (exportType: 'default', jsxRuntime: 'automatic', plugins: [jsx], typescript: true). Use it to add svgo, change the export type, set default props, and so on:

svgReact({
    svgrOptions: {
        icon: true,
        svgProps: { role: 'img' },
    },
});

Note that the merge is shallow, so if you pass plugins, include @svgr/plugin-jsx (and put it last) to keep JSX output working:

import svgReact from '@acusti/vite-plugin-svg-react';
import jsx from '@svgr/plugin-jsx';
import svgo from '@svgr/plugin-svgo';

svgReact({ svgrOptions: { plugins: [svgo, jsx] } });

Why the dev JSX runtime in dev matters

When serving (vite dev), the plugin compiles JSX against react/jsx-dev-runtime; when building, against react/jsx-runtime. This matches what Vite’s main transform pipeline does for your app’s own components, it isn’t configurable, and it’s the plugin’s hard-won correctness feature.

Here’s why: Vite’s dependency scanner treats .svg imports as assets and never crawls the virtual modules this plugin creates. If your app imports React only via JSX, the scanner discovers react/jsx-dev-runtime from your components at startup — but nothing else imports react/jsx-runtime in dev. If the SVG components were compiled against the production runtime, react/jsx-runtime would be a dependency that only these uncrawlable virtual modules import, so on a cold optimizer cache it gets discovered mid-first-request, forcing a re-optimization while the first request is in flight.

In SSR environments (e.g. @cloudflare/vite-plugin’s workerd runtime), that mid-request re-optimization bumps the ?v= hash of every optimized chunk under the in-flight render, splitting React into two module instances, which fails with errors like Cannot read properties of null (reading 'useContext') — a 500 on the first cold request. Emitting the dev runtime in dev keeps these modules on the same optimized dependency graph as the rest of your app, so they never trigger that path.

As defense in depth, SSR users can additionally pin the React family in their server environment’s optimizeDeps so the optimizer never discovers anything React-related late:

environments: {
    ssr: {
        optimizeDeps: {
            include: [
                'react',
                'react/jsx-runtime',
                'react/jsx-dev-runtime',
                'react-dom/server',
            ],
        },
    },
},

FAQ

Why ?react and not import attributes (with { type: 'react' })?

Three reasons:

  1. TypeScript types modules by their specifier string, so declare module '*.svg?react' gives every ?react import the right component type. Import attributes are invisible to the type system: there’s no way to say “*.svg imported with type: 'react' is a component, but plain *.svg is a URL string.”
  2. Hosts are spec-required to throw on unknown attribute types, and Vite’s dev server serves your modules as near-native ESM, rewriting only the specifiers. A custom import attribute would reach the browser intact and hard-fail there.
  3. Query suffixes are Vite’s own blessed convention for import transforms (?url, ?raw, ?inline), so ?react behaves like the rest of the ecosystem and composes with Vite’s asset handling.