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

@paulcoyle/svg-to-react

v1.5.0

Published

An opinionated utility for converting SVG files to React components.

Readme

svg-to-react

npm version

@paulcoyle/svg-to-react is an "opinionated" (read: not exceedingly configurable and primarily built to serve my own needs) utility that will convert SVGs to React components. There are other utilities that do this and likely do it much better but lacked providing a means to hook-in to the React side of the generated components.

Usage

Run by providing an input directory and an output directory:

npx @paulcoyle/svg-to-react <input-directory> <output-directory>

This will convert any *.svg files in the input directory to React components in the output directory using the default configuration. Currently this only searches the direct contents of the input directory and does not descend recursively.

To provide a configuration file, pass the path to the file with either -c or --config:

npx @paulcoyle/svg-to-react -c <config-path> <input-directory> <output-directory>

Formatting

This utility will format the resulting components and index files using Prettier. The nearest Prettier configuration file to the output directory will be used. If no configuration file is found, the Prettier defaults are used.

Configuration

Configuration files are defined in JSON and take the following form:

export type Config = {
  preProcess: {
    /**
     * Used to set attribute values given some condition.
     */
    set: {
      attrs: Record<string, string>
      when?: {
        attr: string
        matches: string
        remove?: boolean
      }
    }[]
    /**
     * Used to replace strings in the raw SVG.
     */
    replace: [string, string][]
    /**
     * Used to simply remove attributes from all elements.
     */
    remove: string[]
  }
  convert: {
    /**
     * The template to use when assembling the React component.
     */
    componentTemplate: string[]
  }
  finalize: {
    /**
     * The template to use to assemble a module index file.
     */
    indexTemplate: string[]
  }
}

Preprocessing Steps

Several preprocessing steps are done on SVGs before they are converted to React components. The following steps are configurable and are performed in a particular order:

  1. "Remove" transforms: removes attributes listed in the config under preProcess.remove.
  2. "Set" transforms: sets particular attribute-value pairs on elements when certain conditions are met.
  3. "Replacement" transforms: replaces values in the SVG, treated as a string.

React-Escapes

A special step is performed after all of these which is called a React-escape. Essentially, it is a way to emit code into attributes on elements in the SVG rather than just strings. To use a React-escape, simply format the attribute value like so: react::(VALUE_HERE).

This would manifest like so:

<svg>
  <path d="react::(drawPath())" />
</svg>
const YourComponent = () => {
  return (
    <svg>
      <path d={drawPath()} />
    </svg>
  )
}

This is often useful in combination with the "Set" preprocessing step documented below.

Preprocess Step: Removal

Any attributes listed in the preProcess.remove configuration array will be removed from all elements.

For example, with the following configuration

{
  "preProcess": {
    "remove": ["fill", "stroke"]
  }
}

The transformation is

<!-- Input -->
<svg>
  <path fill="#ff00ff" stroke="#000000" d="..." />
  <path stroke="#000000" d="..." />
  <path fill="#ff00ff" d="..." />
</svg>

<!-- Output -->
<svg>
  <path d="..." />
  <path d="..." />
  <path d="..." />
</svg>

Preprocess Step: Set

This step allows for setting particular attribute-value pairs on an element when certain conditions are met. Conditions for each "set" application are contained in the when portion of their configuration. Note that specifying a when condition is completely optional and, when omitted, causes the attribute-value pair to be set on every element.

A single "set" configuration is broken down like so:

type SetStep = {
  /**
   * The attribute-value pairs to set.
   * Note that these may include variables from capture groups in `when.matches`
   */
  attrs: Record<string, string>
  when?: {
    /**
     * The attribute to inspect on the element.
     */
    attr: string
    /**
     * A regular expression which, when providing a match, causes the
     * attribute-value pairs to be set.
     * Note that this can contain capture groups to be used in conjunction with
     * `attrs`.
     */
    matches: string
    /**
     * When `true`, will cause the `attr` to be removed when there is a
     * positive match.
     */
    remove?: boolean
  }
}

This can be very useful, especially when paired with React-escapes.

For example, with the following configuration

{
  "preProcess": {
    "set": [
      {
        "attrs": {
          "data-id": "react::(registerId('$1'))"
        },
        "when": {
          "attr": "class",
          "matches": "elem-id-([0-9+])"
        }
      }
    ]
  }
}

The transformation is

<!-- Input -->
<svg>
  <path class="primary elem-id-90210" d="..." />
</svg>

<!-- Output (SVG) -->
<svg>
  <path
    class="primary elem-id-90210"
    data-id="react::(registerId('90210'))"
    d="..."
  />
</svg>

The resulting component might look something like

const YourComponent = () => {
  return (
    <svg>
      <path
        class="primary elem-id-90210"
        data-id={registerId('90210')}
        d="..."
      />
    </svg>
  )
}

Preprocess Step: Replace

While the prior steps work on an AST representation of the SVG, this step treats the whole SVG as a string and allows you do do any replacements you want. You may use regular expressions and capture groups here.

For example, with the following configuration

{
  "preProcess": {
    "replace": [
      ["sensitive-value", "xxxxxxxx"],
      ["<!-- TODO: (.+?) -->", ""]
    ]
  }
}

The transformation is

<!-- Input -->
<svg>
  <!-- TODO: remove sensitive data in our SVGs -->
  <path d="..." data-user="password:sensitive-value" />
</svg>

<!-- Output -->
<svg>

  <path d="..." data-user="password:xxxxxxxx" />
</svg>

Non-Configurable Processing Steps

Several SVGO plugins are applied every time: convertShapeToPath, convertPathData, convertTransform, and removeTitle.

Templates

In order to complete the transformation from SVG to a React component, the transformed SVG markup is injected into a component template. Once all SVGs are converted to components, an index file is generated by injecting all the converted (relative) file paths and their associated component names into the index template. Templates are in EJS so you will usually interpolate values with <%- value %>.

Component Template

Component templates have the following values injected into them:

  • content - the content to be returned by the component
  • componentName - the name of the component taken from the original SVG file name capitalized and converted from kebab-case to CamelCase if required
  • tsRelativeImportPath - the relative import path to the index (rarely used for component templates)
  • path - the path where the component will be written
  • name - the base name of the original SVG file

Here is an example component template:

import { cloneElement, forwardRef } from 'react'

export const <%- componentName %> = forwardRef<SVGSVGElement>(function <%- componentName %>(props, ref) {
  return cloneElement(<%- content %>, { ...props, ref })
})

In the config file, this would be entered as

{
  "convert": {
    "componentTemplate": [
      `import { cloneElement, forwardRef } from 'react'`,
      ``,
      `export const <%- componentName %> = forwardRef<SVGSVGElement>(function <%- componentName %>(props, ref) {`,
      `  return cloneElement(<%- content %>, { ...props, ref })`,
      `})`,
      ``
    ]
  }
}

Index Template

The index template receives components, an array of the component data described in the component template above.

Here is an example index template:

<% components.forEach(function(component) { -%>
  export { <%- component.componentName %> } from '<%- component.tsRelativeImportPath %>'
<% }); -%>

In the config file, this would be entered as

{
  "finalize": {
    "indexTemplate": [
      `<% components.forEach(function(component) { -%>`,
      `  export { <%- component.componentName %> } from '<%- component.tsRelativeImportPath %>'`,
      `<% }); -%>`
    ]
  }
}

Prior Art