Importing SVG into Meteor/React

Hey guys,
since it is “trendy” to import SVGs directly into your code, I’m wondering if anyone from you has some instructions for Meteor and React? I’ve used babel-plugin-inline-react-svg before, but this plugin stopped suddenly working after upgrading a few packages. After some research I found SVGR, which seems to be perfect, but there is not babel or Meteor integration available.

I don’t remember adding any babel plugins to enable inline svg tags in our meteor projects using react.

Are you encountering any errors?

[update] Sorry, I just realized you meant “Import”

Yes it’s trendy (and therefore incredibly annoying :joy:)

The answer is actually very simple you don’t need any plug-ins or anything special because the browser will do it all for you. The issue here is the mime type and the way it renders with a meteor / react stack. I went around and around in circles trying to debug this.

The answer was super easy - just use <img src="images/logo.svg" />

Make sure the encoding on that svg is encapsulated in the svg markup like this via Adobe illustrator or similar:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">

Otherwise if you really need an svg in your code you can actually just do a require to the file and it will be accessible as an object. I heard some people do something with that. Not me.

1 Like

for small svgs that i want to inline and/or make configurable (e.g. change colors or other properties) i do the following workflow:

  • copy the svg code
  • paste it here to optimize it SVGOMG - SVGO's Missing GUI
  • make a new react file, paste it there as a react component, vscode normally formats it properly so that its actually a react representation

that way you can manipulate your svg, which is sometimes handy.

this is of course not suitable if you need to add a ton of svgs.

I would also only inline small svgs that are part of important layout. Large svgs that are more like pictures should be loaded via <img /> tag

Ok for react try this…

your context

'use strict'

Object.defineProperty(exports, '__esModule', { value: true })
const React = require('react')
exports.DefaultContext = {
  color: undefined,
  size: undefined,
  className: undefined,
  style: undefined,
  attr: undefined
}

exports.IconContext = React.createContext && React.createContext(exports.DefaultContext)

You will require your context in this generator:

/* eslint-disable */
let __assign = (this && this.__assign) || function () {
  __assign = Object.assign || function (t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
      s = arguments[i]
      for (var p in s) {
        if (Object.prototype.hasOwnProperty.call(s, p)) {
          t[p] = s[p]
        }
      }
    }
    return t
  }
  return __assign.apply(this, arguments)
}
let __rest = (this && this.__rest) || function (s, e) {
  var t = {}
  for (var p in s) {
    if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) { t[p] = s[p] }
  }
  if (s != null && typeof Object.getOwnPropertySymbols === 'function') {
    for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) { t[p[i]] = s[p[i]] }
  }
  return t
}
Object.defineProperty(exports, '__esModule', { value: true })
const React = require('react')
const iconContext_1 = require('./iconContext')
const Tree2Element = tree => {
  return tree && tree.map(function (node, i) { return React.createElement(node.tag, __assign({ key: i }, node.attr), Tree2Element(node.child)) })
}
const GenIcon = data => {
  return function (props) { return (React.createElement(IconBase, __assign({ attr: __assign({}, data.attr) }, props), Tree2Element(data.child))) }
}

const IconBase = props => {
  const elem = conf => {
    var computedSize = props.size || conf.size || '1em'
    var className
    if (conf.className) { className = conf.className }
    if (props.className) { className = (className ? className + ' ' : '') + props.className }
    let attr = props.attr; let svgProps = __rest(props, ['attr'])
    return (React.createElement('svg', __assign({ stroke: 'currentColor', fill: 'currentColor', strokeWidth: '0' }, conf.attr, attr, svgProps, { className: className, style: __assign({ color: props.color || conf.color }, conf.style, props.style), height: computedSize, width: computedSize }), props.children))
  }
  return iconContext_1.IconContext !== undefined
    ? React.createElement(iconContext_1.IconContext.Consumer, null, function (conf) { return elem(conf) })
    : elem(iconContext_1.DefaultContext)
}

export { GenIcon, IconBase }
/* eslint-enable */

and now declare your svgs as constants such as:

import { GenIcon } from './IconGenerator'

const SomeImage = props => {
  return GenIcon({
    tag: 'svg',
    attr: { version: '1.2', baseProfile: 'tiny', viewBox: '0 0 24 24' },
    child: [{
      tag: 'path',
      attr: { d: 'M13 20c-.802 0-1.555-.312-2.122-.879l-7.121-7.121 7.122-7.121c1.133-1.133 3.11-1.133 4.243 0 .566.566.878 1.32.878 2.121s-.312 1.555-.879 2.122l-2.878 2.878 2.878 2.879c.567.566.879 1.32.879 2.121s-.312 1.555-.879 2.122c-.566.566-1.319.878-2.121.878zm-6.415-8l5.708 5.707c.378.378 1.037.377 1.414 0 .189-.189.293-.439.293-.707s-.104-.518-.293-.707l-4.292-4.293 4.292-4.293c.189-.189.293-.44.293-.707s-.104-.518-.293-.707c-.378-.379-1.037-.378-1.414-.001l-5.708 5.708z' }
    }]
  })(props)
}
export { SomeImage }

You image now.

import { SomeImage } from .....

<div>
   <SomeImage style={{ color... fontSize .... verticalAlign ... etc }} />
<div>


You have multiple children in your svg, you can target them for different styling (color, stroke etc)

1 Like

Example with dynamic styling:
This is a football t-shirt with stripes. Can change colors via props.

import { GenIcon } from '../../../../../components/shared/Icons/IconGenerator'

const TwOneF = props => {
  return GenIcon({
    tag: 'svg',
    attr: {
      fill: 'none',
      stroke: 'currentColor',
      strokeWidth: '1',
      viewBox: '0 0 400 343'
    },
    child: [
      {
        tag: 'path',
        attr: {
          fill: props.stroke,
          stroke: 'none',
          d: 'M 150.73 0.343 C 150.73 0.532 149.957 0.687 149.013 0.687 C 147.716 0.687 147.296 0.813 147.296 1.202 C  .... '
        }
      },
      {
        tag: 'path',
        attr: {
          stroke: 'none',
          fill: props.basec,
          d: 'M 159.313 5.521 C 159.313 7.328 163.953 13.047 165.419 13.047 C 165.591 13.047 166.088 13.356 166.524 13.734 C 166.959 ....'
        }
      },
      {
        tag: 'path',
        attr: {
          fill: props.onec,
          stroke: 'none',
          d: 'M 159.128 3.8 C 159.003 4.001 158.994 4.259 159.107 4.372 C 159.221 4.485 159.313 4.398 159.313 4.177 C 159.313  .... '
        }
      }
    ]
  })(props)
}

The component:

<TwOneF
        stroke={stroke}
        basec={baseC || 'white'}
        onec={oneC || 'white'}
        style={{ fontSize: 220 }} />

This disabled browser level caching for me so I figured its only for animated svgs / a dynamic canvas etc, did you manage to get it to cache?

Nice man I figured it would have some use from a practical dynamic implementation. I’ve only needed it for crisp logos so far but will keep this code snippet in the arsenal as an option.

its then part of the js bundle, which is usually cached

Hey,
thank you all for the answers. I was hoping that there is a plugin solution like babel-plugin-inline-react-svg which converts the imported SVG automatically into a React component. Now I’ve started using SVGR Playground - SVGR to convert all SVGs into React components. Good thing about it that you can check and optimize each SVG seperately, because some SVGs may cause issues when using ids and gradients.