React Import SVG inline from URL

11,594

Solution 1

You should fetch svg image and paste it in html

const SVG_URL = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/106114/tiger.svg';

class Svg extends React.Component {
  state = {
    svg: null,
    loading: false,
  }

  componentDidMount() {
    fetch(this.props.url)
      .then(res => res.text())
      .then(text => this.setState({ svg: text }));
  }

  render() {
    const { loading, svg } = this.state;
    if (loading) {
      return <div className="spinner"/>;
    } else if(!svg) {
      return <div className="error"/>
    }
    return <div dangerouslySetInnerHTML={{ __html: this.state.svg}}/>;
  }
}

ReactDOM.render(
  <Svg url='https://s3-us-west-2.amazonaws.com/s.cdpn.io/106114/tiger.svg' />,
  document.querySelector("#app")
);

you can can edit example here https://jsfiddle.net/anu6x3bk/

React does not recommend using dangerouslySetInnerHTML so you should use some package like svg-inline-react

Solution 2

@ivnkld answer pointed me to the right direction, thanks! And for Googlers such as I was, here is implementation of the same approach with hooks:

import React, { useEffect, useState } from 'react';

const SvgInline = props => {
    const [svg, setSvg] = useState(null);
    const [isLoaded, setIsLoaded] = useState(false);
    const [isErrored, setIsErrored] = useState(false);

    useEffect(() => {
        fetch(props.url)
            .then(res => res.text())
            .then(setSvg)
            .catch(setIsErrored)
            .then(() => setIsLoaded(true))
    }, [props.url]);

    return (
        <div 
            className={`svgInline svgInline--${isLoaded ? 'loaded' : 'loading'} ${isErrored ? 'svgInline--errored' : ''}`}
            dangerouslySetInnerHTML={{ __html: svg }}
        />
    );
}

Solution 3

For those who want to avoid using dangerouslySetInnerHTML please check the following example. Once we get the svg as text from the api, we will use DOMParser to convert the text to Document object. We will then use React.createElement to render the image (or) icon.

// utils/convertDocEleToReact.js
import { createElement } from "react";

const innerFunction = (element, props) => {
    const tagName = element.tagName;
    let _props = props || {};

    for(let i = 0; i < element.attributes.length; i++){
      _props[element.attributes[i].nodeName] = element.attributes[i].nodeValue;
    }

    let children = Array.from(element.children).map(item => innerFunction(item));

    return createElement(tagName, _props, children);
};

export const convertDocEleToReact = (element, props) => {
    try{
       return innerFunction(element, props);
    }
    catch(ex){
      return createElement("span", {}, "Error loading svg image");
    }
};
// components/SVGUrl.js
import React from "react";
import { convertDocEleToReact } from "./utils/convertDocEleToReact";

export const SVGUrl = ({ ...props }) => {
    const [ Comp, setComp ] = useState(null);
    const [loading, setLoading] = useState(false);

    useEffect(_ => {
        setLoading(true);
        fetch(/** svg url */)
          .then(res => res.text())
          .then(res => {
              const domParser = new DOMParser();
              const ele = domParser.parseFromString(res, "image/svg+xml");
              setComp(convertDocEleToReact(ele.documentElement, props));
              setLoading(false);
          });
    }, []);

    return loading ? "..." : Comp;
};
Share:
11,594
Mikko Lasso
Author by

Mikko Lasso

Updated on July 22, 2022

Comments

  • Mikko Lasso
    Mikko Lasso almost 2 years

    I am working on an issue where SVG image needs to be loaded from URL(AWS S3) to a react component. I am able to successfully show and load image using the SVG-inline react component from a local file. However, the svg files needs to be loaded inline from S3 bucket, JS svg import does not work with URLs.

    So I am trying to find the best alternative solution for this?

  • Marko Šutija
    Marko Šutija over 2 years
    No need for external package since svg-inline-react also use dangerouslySetInnerHTML: github.com/sairion/svg-inline-react/blob/master/src/index.js