npm package: @mapbox/svg-react-transformer
Description: Transform SVG into JSX or React component modules
Author: Mapbox
Latest version: 2.1.0

# @mapbox/svg-react-transformer

Transform SVGs into JSX or React component modules.

This module takes one string (the SVG) and converts it to another string (the JSX or React component module).
That low-level focus means it can be used by a variety of higher-level modules that target specific contexts.
Examples:

- [svg-react-transformer-writer](../svg-react-transformer-writer): A Node API and CLI for reading SVG files and writing React component module files.
- [svg-react-transformer-loader](../svg-react-transformer-loader): A Webpack loader for transforming SVG files into React component modules.

## Installation

```
npm install @mapbox/svg-react-transformer
```

## API

### toComponentModule

`svgReactTransformer.toComponentModule(svg, [options])`

Returns a Promise that resolves with the React component module string.
Runs an SVG string through [`toJsx`] to get a JSX string, then inserts the JSX into a templated React component module.

#### svg (toComponentModule)

Type: `string`.
**Required**.

The input SVG.

#### options (toComponentModule)

##### options.svgoPlugins (toComponentModule)

Type: `Array<Object>`.

See [the same option for `toInlineSvg`](#optionssvgoplugins-toinlinesvg).

##### options.name (toComponentModule)

Type: `string`.
Default: `'SvgComponent'`.

A name for the React component class.
The value will be converted to PascalCase (e.g. `fancy-pants -> FancyPants`) and then passed as the `id` option to [`toJsx`].

##### options.propTypes (toComponentModule)

Type: `string`.

A *stringified* object defining `propTypes` for the generated React component.
It should be the string of the code that you'd put in here: `MyComponent.propTypes = ${this.place}`, e.g. `'{ title: PropTypes.string.isRequired }'`.

This value will be passed to your selected template.
If this value is defined, the built-in templates will include `const PropTypes = require('prop-types');`.

To programmatically convert a real object to a string that works here, I suggest using [stringify-object].

##### options.defaultProps (toComponentModule)

Type: `string`.

A *stringified* object defining `defaultProps` for the generated React component.
It should be the string of the code that you'd put in here: `MyComponent.defaultProps = ${this.place}`, e.g. `'{ title: 'Untitled' }'`.

This value will be passed to your selected template.

To programmatically convert a real object to a string that works here, I suggest using [stringify-object].

##### options.template (toComponentModule)

Type: `Function | 'default' | 'fancy'`.
Default: `'default'`.

If the value is a `string`, it can be one of the [component module template](#component-module-templates) values described below: [`default`](#default-template), [`fancy`](#fancy-template).

If the value is a `function`, it must be a [custom template function].

##### options.precompile (toComponentModule)

Type: `boolean`.
Default: `false`.

If `true`, the template will be passed through Babel (with the ES2015 and React presets), so you don't have to compile it yourself.

## Component module templates

### `default` template

The default template creates a module exporting a `React.PureComponent` that renders the SVG element and its children as React elements.

This template is simple and unopinionated.

Here's an example generated by the default template.

```
"use strict";
const React = require("react");

class SvgComponent extends React.PureComponent {
  render() {
    return (
      <svg {...this.props} viewBox="0 0 18 18">
        <path d="M7 4l1.6 4H5.5S4.1 6 3 6h-.8L3 8l1 3h4.6L7 15h2l3.2-4H14c1 0 2-.7 2-1.5S15 8 14 8h-1.8L9 4H7z" />
      </svg>
    );
  }
}

SvgComponent.defaultProps = {
  role: "img",
  focusable: "false"
};

module.exports = SvgComponent;
```

### `fancy` template

This is an opinionated template with a few nice features:

- Applies some accessibility patterns.
  Adds `aria-hidden` and `focusable="false"` to the `<svg>` element, and exposes an `alt` prop for alternative text (hidden from sight, legible for screen readers).
- Uses the SVG's `viewbox` attribute to determine an aspect ratio, and applies a wrapper `<div>` and CSS so that the element can have a fluid width while preserving its aspect ratio (across browsers IE11+).
- Adds extra SVGO plugins `removeTitle`, `removeStyleElement`, and `removeAttrs` to remove `width` and `height` from the `<svg>` element.
  The `alt` prop provides better accessibility than titles for almost all use cases; `<style>` elements within SVGs can be dangerous for inlined SVG; and width and height will be determined by the SVG's containing context.
- Exposes `containerClassName`, `containerStyle`, `svgClassName`, and `svgStyle` props for customizing the whole thing.

Here is an example generated by the fancy template:

```js
"use strict";
const React = require("react");
class Fakery extends React.PureComponent {
  render() {
    const containerStyle = this.props.containerStyle || {};
    if (!containerStyle.position || containerStyle.position === "static") {
      containerStyle.position = "relative";
    }
    containerStyle.paddingBottom = "100%";
    const svgStyle = this.props.svgStyle || {};
    svgStyle.position = "absolute";
    svgStyle.overflow = "hidden";
    svgStyle.top = 0;
    svgStyle.left = 0;
    svgStyle.width = "100%";
    const text = !this.props.alt
      ? null
      : <div style={{ position: "absolute", left: -9999 }}>
          {this.props.alt}
        </div>;
    return (
      <div style={containerStyle} className={this.props.containerClassName}>
        <svg
          aria-hidden={true}
          focusable="false"
          style={svgStyle}
          className={this.props.svgClassName}
          viewBox="0 0 18 18"
        >
          <g
            style={{
              marginTop: "0"
            }}
          >
            <path d="M3.4 9.2c.1-1.1.4-2.3 1.5-3.2.9-.7 1.8-.8 2.9-.5 1.4.5 1 .5 2.4 0 1.5-.6 3 0 3.7.8.1.2.2.3 0 .4-1.8 1.3-1.6 3.9.3 5 .2.1.3.2.2.4-.4 1-1 1.9-1.7 2.7-.5.5-1.1.7-1.8.4-.1 0-.3-.1-.4-.1-.9-.1-1.7-.1-2.5.2-.3.1-.5.2-.8.3-.4.1-.8-.1-1.1-.4-.7-.5-1.1-1.2-1.5-1.9-.7-1.1-1.2-2.7-1.2-4.1" />
            <path d="M11.6 2.5c0 1.2-1 2.4-2 2.7-.6.1-.8 0-.7-.6.2-1.2 1.2-2.2 2.5-2.5.2 0 .2 0 .2.2v.2" />
          </g>
        </svg>
        {text}
      </div>
    );
  }
}
module.exports = Fakery;
```

## Custom template functions

Custom template functions receive one argument, a data object, and must return a string.
The data object argument includes the following properties:

- `name`: The value of the `name` option above (converted to PascalCase).
- `propTypes`: The value of [the `propTypes` option](#optionsproptypes-tocomponentmodule), above.
- `defaultProps`: The value of [the `defaultProps` option](#optionsdefaultprops-tocomponentmodule) above.
- `jsxSvg`: The JSX string generated from your source SVG.
- `inlineSvg`: Your source SVG processed by SVGO for use inline with HTML.
  (In a template you could use this with `dangerouslySetInnerHTML`.)

## What about other modules that do similar things?

There are many npm packages for converting SVGs to React components:
Webpack loaders, Browserify transforms, CLIs, Gulp plugins, Brunch plugins, etc.
They are all addressing the same problem but formatting their output differently.
However, their APIs seems to be too specialized for them to share logic, so they end up reimplementing a lot of the same stuff in different ways.

There are only a few steps to accomplish what we all want:

1. Optimize the SVG with SVGO.
2. Transform the SVG to JSX (or a React element object).
3. Plug the JSX into a React component module.

Then you need an API that allows the user to configure these steps; that is, to specify SVGO plugins and control the React component output.

So that's the goal of this package: provide an API to accomplish those steps (without unnecessarily reimplementing functionality that (should) belong to other packages).
Ideally, then, this package could be *used* by Webpack loaders, Browserify transforms, CLIs, Gulp plugins, Brunch plugins, etc., and save them from reimplementing the same functionality over and over again.

### toJsx

`svgReactTransformer.toJsx(svg, [options])`

Returns a Promise that resolves with the JSX string.
Runs an SVG through [`toInlineSvg`], then converts the SVG to JSX.

#### svg (toJsx)

Type: `string`.
**Required**.

The input SVG.

#### options (toJsx)

Any of the [options for `toInlineSvg`](#options-toinlinesvg).
These are passed directly to that function.

### toInlineSvg

`svgReactTransformer.toInlineSvg(svg, [options])`

Returns a Promise that resolves with your SVG processed by SVGO so that it works inline within HTML and is otherwise optimized.
You could use the result with `dangerouslySetInnerHTML`, for example.
It's also used internally by [`toComponentModule`].

#### svg (toInlineSvg)

Type: `string`.
**Required**.

The input SVG.

#### options (toInlineSvg)

##### options.svgoPlugins (toInlineSvg)

Type: `Array<Object>`.

Configuration for [SVGO](https://github.com/svg/svgo) plugins.

The following are automatically set (but can be overridden) because they are important for SVGs that will be inserted inline into an HTML document:

```js
[
  { removeDoctype: true },
  { removeComments: true },
  { removeXMLNS: true },
  {
    // svgId is determined by the `id` option, below.
    cleanupIDs: { prefix: svgId + '-' }
  }
]
```

##### options.id (toInlineSvg)

Type: `string`.
Default: a [`cuid`](https://github.com/ericelliott/cuid)-generated string.

Used by SVGO's `cleanupIDs` plugin to scope `id` attributes.
Any characters other than `/[a-zA-Z0-9]/` will be stripped.

[`toinlinesvg`]: #toinlinesvg

[`tojsx`]: #tojsx

[`tocomponentmodule`]: #tocomponentmodule

[stringify-object]: https://www.npmjs.com/package/stringify-object

[custom template function]: #custom-template-functions