@mapbox/svg-react-transformer

2.1.0 • Public • Published

@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:

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.

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 values described below: default, fancy.

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:

"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, above.
  • defaultProps: The value of the defaultProps option 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. 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 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:

[
  { 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-generated string.

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

Package Sidebar

Install

npm i @mapbox/svg-react-transformer

Weekly Downloads

82

Version

2.1.0

License

MIT

Unpacked Size

108 kB

Total Files

22

Last publish

Collaborators

  • mbx-npm-ci-production
  • mbx-npm-ci-staging
  • mbx-npm-advanced-actions-production
  • mbx-npm-advanced-actions-staging
  • mbx-npm-09-production
  • mbx-npm-08-production
  • mbx-npm-07-production
  • mbx-npm-06-production
  • mbx-npm-05-production
  • mbx-npm-04-production
  • mbx-npm-03-production
  • mbx-npm-02-production
  • mbx-npm-01-production
  • mbx-npm-02-staging
  • mapbox-npm-01
  • mapbox-npm-02
  • mapbox-npm-07
  • mapbox-npm-03
  • mapbox-npm-04
  • mapbox-npm-09
  • mapbox-npm-05
  • mapbox-npm-06
  • mapbox-npm-08
  • mapbox-npm-advanced-actions
  • mapbox-npm-ci
  • mapbox-npm
  • mapbox-admin
  • mapbox-machine-user