1.0.3 • Published 3 years ago

react-external-svg v1.0.3

Weekly downloads
2
License
MIT
Repository
github
Last release
3 years ago

react-external-svg

A simple tool to load external SVG files and convert to react elements and attach props, styles and methods.

Use when you:
  • Have no access to the svg at build time.
  • Need to dynamically accept an svg at run time.
  • Need to be able to apply static or dynamic properties and styling to the loaded svg.
Example use case:
  • Users upload an svg logo
  • Logo can be customized within your online logo editor react app
  • The updated logo can then be downloaded by the user

This tool was created for a very specific use case and has an unnecessary overhead if you can build the SVG directly into your codebase, in which case i would recommend manually converting your svg to jsx or importing as a module with CRA or converting with something like SVGR

Usage

npm install react-external-svg

Basic usage

import { ExternalSVG } from 'react-external-svg';

export cont MySvg = () => {

  return <ExternalSVG 
    src="https://my-server.com/someimage.svg"
    applyStyles={{
      Layer1: {
        width: "100%",
        height: "100%"
        }
    }} 
    applyProps={{
      myPath: {
        fill: "#bada33"
        }
    }} 
  />
}

Applying props and styles

Example:
// Example SVG passed to ExternalSVG src
<svg id="Layer_1" x="0px" y="0px" viewBox="0 0 208 105.7" >
    <g id="lines_group">
        <polyline id="polyline1" points="59,26.3 59,2 84.3,2 "/>
        <polyline points="83.3,79.3 59,79.3 59,46.7 "/>
        <polyline points="136.3,45.4 136.3,79.3 111.7,79.3 "/>
        <polyline points="112,2 136.3,2 136.3,25.3 "/>
    </g>
</svg>
In this example I can pass a width and height property to the 'Layer_1' element by passing:
applyProps={{
  Layer_1:{
    width: '100%',
    height: '100%'
  }
}}
Now if i wanted to apply some props to all the polyline elements within the 'lines_group' element, i could do so by referencing these by their tag identifier - eg.
  applyProps={{
    'Layer_1>lines_group>polyline':{
      fill: 'bada33'
    }
  }}
I can also mix and match these identifiers as I like, remembering to use > to delineate each identifier - eg.
applyProps={{
  'Layer_1>g>polyline':{
    fill: 'bada33'
  }
}}

// or

applyProps={{
  'svg>lines_group>polyline':{
    fill: 'bada33'
  }
}}
Aside from element id or tagName we can also reference our element by the child(number) identifier for more targeted identification. eg.
//Apply the style only to the second polyline element
applyProps={{
  'svg>g>child1':{
    fill: 'bada33'
  }
}}

// or

applyProps={{
  'child0>child0>child1':{
    fill: 'bada33'
  }
}}

Dynamic props and styles

Example src
const src = 'https://upload.wikimedia.org/wikipedia/commons/a/a7/React-icon.svg'

//src result
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_2_1_" x="0px" y="0px" viewBox="0 0 841.9 595.3" enable-background="new 0 0 841.9 595.3" xml:space="preserve" wtx-context="37D13676-EFEB-45EF-9AAC-DFF815FC193D">
  <g>
    <path fill="#61DAFB" d="M666.3,296.5c0-32.5-40.7-63.3-103.1-82.4c14.4-63.6,8-114.2-20.2-130.4c-6.5-3.8-14.1-5.6-22.4-5.6v22.3   c4.6,0,8.3,0.9,11.4,2.6c13.6,7.8,19.5,37.5,14.9,75.7c-1.1,9.4-2.9,19.3-5.1,29.4c-19.6-4.8-41-8.5-63.5-10.9   c-13.5-18.5-27.5-35.3-41.6-50c32.6-30.3,63.2-46.9,84-46.9l0-22.3c0,0,0,0,0,0c-27.5,0-63.5,19.6-99.9,53.6   c-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7,0,51.4,16.5,84,46.6c-14,14.7-28,31.4-41.3,49.9c-22.6,2.4-44,6.1-63.6,11   c-2.3-10-4-19.7-5.2-29c-4.7-38.2,1.1-67.9,14.6-75.8c3-1.8,6.9-2.6,11.5-2.6l0-22.3c0,0,0,0,0,0c-8.4,0-16,1.8-22.6,5.6   c-28.1,16.2-34.4,66.7-19.9,130.1c-62.2,19.2-102.7,49.9-102.7,82.3c0,32.5,40.7,63.3,103.1,82.4c-14.4,63.6-8,114.2,20.2,130.4   c6.5,3.8,14.1,5.6,22.5,5.6c27.5,0,63.5-19.6,99.9-53.6c36.4,33.8,72.4,53.2,99.9,53.2c8.4,0,16-1.8,22.6-5.6   c28.1-16.2,34.4-66.7,19.9-130.1C625.8,359.7,666.3,328.9,666.3,296.5z M536.1,229.8c-3.7,12.9-8.3,26.2-13.5,39.5   c-4.1-8-8.4-16-13.1-24c-4.6-8-9.5-15.8-14.4-23.4C509.3,224,523,226.6,536.1,229.8z M490.3,336.3c-7.8,13.5-15.8,26.3-24.1,38.2   c-14.9,1.3-30,2-45.2,2c-15.1,0-30.2-0.7-45-1.9c-8.3-11.9-16.4-24.6-24.2-38c-7.6-13.1-14.5-26.4-20.8-39.8   c6.2-13.4,13.2-26.8,20.7-39.9c7.8-13.5,15.8-26.3,24.1-38.2c14.9-1.3,30-2,45.2-2c15.1,0,30.2,0.7,45,1.9   c8.3,11.9,16.4,24.6,24.2,38c7.6,13.1,14.5,26.4,20.8,39.8C504.7,309.8,497.8,323.2,490.3,336.3z M522.6,323.3   c5.4,13.4,10,26.8,13.8,39.8c-13.1,3.2-26.9,5.9-41.2,8c4.9-7.7,9.8-15.6,14.4-23.7C514.2,339.4,518.5,331.3,522.6,323.3z    M421.2,430c-9.3-9.6-18.6-20.3-27.8-32c9,0.4,18.2,0.7,27.5,0.7c9.4,0,18.7-0.2,27.8-0.7C439.7,409.7,430.4,420.4,421.2,430z    M346.8,371.1c-14.2-2.1-27.9-4.7-41-7.9c3.7-12.9,8.3-26.2,13.5-39.5c4.1,8,8.4,16,13.1,24C337.1,355.7,341.9,363.5,346.8,371.1z    M420.7,163c9.3,9.6,18.6,20.3,27.8,32c-9-0.4-18.2-0.7-27.5-0.7c-9.4,0-18.7,0.2-27.8,0.7C402.2,183.3,411.5,172.6,420.7,163z    M346.7,221.9c-4.9,7.7-9.8,15.6-14.4,23.7c-4.6,8-8.9,16-13,24c-5.4-13.4-10-26.8-13.8-39.8C318.6,226.7,332.4,224,346.7,221.9z    M256.2,347.1c-35.4-15.1-58.3-34.9-58.3-50.6c0-15.7,22.9-35.6,58.3-50.6c8.6-3.7,18-7,27.7-10.1c5.7,19.6,13.2,40,22.5,60.9   c-9.2,20.8-16.6,41.1-22.2,60.6C274.3,354.2,264.9,350.8,256.2,347.1z M310,490c-13.6-7.8-19.5-37.5-14.9-75.7   c1.1-9.4,2.9-19.3,5.1-29.4c19.6,4.8,41,8.5,63.5,10.9c13.5,18.5,27.5,35.3,41.6,50c-32.6,30.3-63.2,46.9-84,46.9   C316.8,492.6,313,491.7,310,490z M547.2,413.8c4.7,38.2-1.1,67.9-14.6,75.8c-3,1.8-6.9,2.6-11.5,2.6c-20.7,0-51.4-16.5-84-46.6   c14-14.7,28-31.4,41.3-49.9c22.6-2.4,44-6.1,63.6-11C544.3,394.8,546.1,404.5,547.2,413.8z M585.7,347.1c-8.6,3.7-18,7-27.7,10.1   c-5.7-19.6-13.2-40-22.5-60.9c9.2-20.8,16.6-41.1,22.2-60.6c9.9,3.1,19.3,6.5,28.1,10.2c35.4,15.1,58.3,34.9,58.3,50.6   C644,312.2,621.1,332.1,585.7,347.1z"/>
    <polygon fill="#61DAFB" points="320.8,78.4 320.8,78.4 320.8,78.4  "/>
    <circle fill="#61DAFB" cx="420.9" cy="296.5" r="45.7"/>
    <polygon fill="#61DAFB" points="520.5,78.1 520.5,78.1 520.5,78.1  "/>
  </g>
</svg>
In this example we will load in the react logo from wikimedia and apply a rotation effect when the icon is clicked with a dynamic rotation property
import React, { useState } from 'react';
import ExternalSVG from "react-external-svg";

const ReactLogo = () => {
  const src = 'https://upload.wikimedia.org/wikipedia/commons/a/a7/React-icon.svg';

  const errorHandler = (err) => {
    console.error(err)
  }

  const [rotation, setRotation] = useState(0);

  const clickHandler = () => {
    setRotation(rotation + 360);
  }

  const applyStyles = {
    'svg>g': {
      transform: `rotate(${rotation}deg)`,
      transition: 'transform 1s',
      transformOrigin: 'center'
    }
  }

  const applyProps = {
    'svg>': {
      width: '50%',
      height: '50%'
    },
    'svg>g': {
      onClick: () => clickHandler()
    },
    'svg>g>path': {
      fill: 'turquoise'
    }
  }

  return (<><ExternalSVG src={src} applyStyles={applyStyles} applyProps={applyProps} onError={errorHandler} /></>)
}

DEMO https://codesandbox.io/s/react-external-svg-demo-ysfci


Props

src: (string) - Accepts either an svg string or url
applyProps: (object) - Accepts an object of keys (element identifier): value: (object) props to apply
applyStyles: (object) - Accepts an object of keys (element identifier): value: (object) style to apply
onError: (function) - Accepts a function: (err) => {}

react-external-svg is loosely based on https://github.com/janjakubnanista/svg-to-jsx please check that out if need a simple tool that takes in SVG and spits out JSX
react-external-svg relies on svg-parser by Rich Harris https://github.com/Rich-Harris/svg-parser