React SVG component as background-image to div

52,650

Solution 1

Unfortunately, what you're trying to achieve is not possible. You'll have to keep your SVG as an actual .svg file (and link to that), or do some CSS trickery to place SVG component behind your foreground-component.

In other words:

<!-- step.svg -->
<svg version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 193.3 129.7">
  <!-- copy paste your svg here -->
</svg>

And in your component:

import stepSvgUrl from "../../components/UI/SVGs/step.
// ...
<Wrap key={step._id} bg={stepSvgUrl}>

When you import an SVG file like that, create-react-app applies a Webpack loader that includes the generated URL to the SVG you're importing - which is fine to pass into the background-image css property.

Hope this helps!

Solution 2

For React SVG background images, I find this works best:

// MyComponent.js

import mySvg from './mySvg.svg';

...

function MyComponent() {
  return (
    <div
       className="someClassName"
       style={{ backgroundImage: `url(${mySvg})` }}
    > 

... etc

The path to the SVG file will change when webpack bundles, so by using the template string you can keep things linked up.

In the CSS class you can do whatever styling you like.

Solution 3

Actually, there is a way to achieve what you want, without the SVG beeing a component:

Here is the updated StepBar component:

import React, { Component } from "react";
import StepSVG from "../../components/UI/SVGs/test.svg";
import encodeSVG from '../../lib/encodeSVG';

class StepBar extends Component {
  render() {
    const { steps } = this.props;

    return (
      <div>
        {steps
          ? steps.map(step => {
              return (
                <Wrap key={step._id} bg={StepSVG}>
                  <P>
                    {" "}
                    <Link to={"/step/" + step._id}>{step.title} </Link>
                  </P>
                </Wrap>
              );
            })
          : null}
      </div>
    );
  }
}

export default StepBar;

const Wrap = styled.div`
  padding: 30px 30px 0 0;
  display: inline-block;
  background-image: ${encodeSVG(bg, '#FF9900')};
`;

Here is encodeSVG lib file:

var symbols = /[\r\n"%#()<>?\[\\\]^`{|}]/g;

function addNameSpace(data) {
  if (data.indexOf('http://www.w3.org/2000/svg') < 0) {
    data = data.replace(/<svg/g, "<svg xmlns='http://www.w3.org/2000/svg'");
  }

  return data;
}

function encodeSVG(data) {
  // Use single quotes instead of double to avoid encoding.
  if (data.indexOf('"') >= 0) {
    data = data.replace(/"/g, "'");
  }

  data = data.replace(/>\s{1,}</g, '><');
  data = data.replace(/\s{2,}/g, ' ');

  return data.replace(symbols, encodeURIComponent);
}

var encode = function(svg, fill) {
  if (fill) {
    svg = svg.replace(/<svg/g, `<svg fill="${fill}"`);
  }
  var namespaced = addNameSpace(svg);
  var dimensionsRemoved = namespaced
    .replace(/height="\w*" /g, '')
    .replace(/width="\w*" /g, '')
    .replace(/height='\w*' /g, '')
    .replace(/width='\w*' /g, '');
  var encoded = encodeSVG(dimensionsRemoved);

  var header = 'data:image/svg+xml,';
  var dataUrl = header + encoded;  

  return `url("${dataUrl}")`;
};

You import the svg, and use a string loader for svgs with your preferred build system (for example WebPack), pass that svg to encodeSVG, et voila! :)

Solution 4

I ended up using user11011301's solution in combination with raw-loader to load the SVG content. Not the cleanest solution I'm sure, but I couldn't get it to work with other webpack loaders.

import searchIcon from '../../images/search.svg';

<input style={{backgroundImage: `url(data:image/svg+xml;base64,${btoa(searchIcon)})`}}/>

Solution 5

url(`data:image/svg+xml;utf8,${svgTxt}`)

;or

url(`data:image/svg+xml;base64,${btoa(svgTxt)}`)

that's it.

Share:
52,650

Related videos on Youtube

user8467470
Author by

user8467470

Updated on July 09, 2022

Comments

  • user8467470
    user8467470 almost 2 years

    I am trying to get styled-components to take an SVG that is a react component and set it as a background-image but I get the error:

    TypeError: Cannot convert a Symbol value to a string

    SVG component code:

    import React from "react";
    
    const testSVG = props => {
      return (
        <svg version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 193.3 129.7">
          <g>
            <g>
              <g>
                <path
                  fill="#434343"
                  class="st0"
                  d="M162.4,116c-4.6,7.5-15.6,13.6-24.4,13.6H16c-8.8,0-12.3-6.2-7.7-13.7c0,0,4.6-7.7,11.1-18.4
                    c4.4-7.4,8.9-14.7,13.3-22.1c1.1-1.8,6.1-7.7,6.1-10.1c0-7.3-15-24.9-18.5-30.7C13.5,23.6,8.3,14.9,8.3,14.9
                    C3.7,7.4,7.2,1.2,16,1.2c0,0,65.9,0,106.8,0c10,0,25.9-4.5,33.5,3.8c8.7,9.3,15.5,25.4,22.5,36.8c2.6,4.2,14.5,18.3,14.5,23.8
                    c-0.1,7.6-16,26.1-19.6,32C167.1,108.3,162.4,116,162.4,116z"
                />
              </g>
            </g>
          </g>
        </svg>
      );
    };
    
    export default testSVG;
    

    Container component code:

    import React, { Component } from "react";
    import StepSVG from "../../components/UI/SVGs/Test";
    
    class StepBar extends Component {
      render() {
        const { steps } = this.props;
    
        return (
          <div>
            {steps
              ? steps.map(step => {
                  return (
                    <Wrap key={step._id} bg={StepSVG}>
                      <P>
                        {" "}
                        <Link to={"/step/" + step._id}>{step.title} </Link>
                      </P>
                    </Wrap>
                  );
                })
              : null}
          </div>
        );
      }
    }
    
    export default StepBar;
    
    const Wrap = styled.div`
      padding: 30px 30px 0 0;
      display: inline-block;
      background-image: url(${props => props.bg});
    `;
    

    I am using create-react-app out of the box.

    I also tried without using styled components and used inline styles in place to no success.

    Is it possible to use an SVG as a background-image in this context with the SVG being a component?

  • user8467470
    user8467470 almost 6 years
    Thanks. This was my original approach but I am trying to render the colors dynamically. I guess I would just have to create N number of static SVGs for each colour in this approach?
  • Kris Selbekk
    Kris Selbekk almost 6 years
    That's one way to do it, but you can still use your original approach. In that case you'd have to layer the SVG below your content with CSS (the z-index prop will help with that).
  • user8467470
    user8467470 almost 6 years
    Thanks Kris, As my original approach just doesn't seem possible for now I will select this as Solved. I appreciate the help.
  • johannes_lalala
    johannes_lalala about 3 years
    the question was about creating an svg as react component, not loading a normal svg from a file
  • Navidot
    Navidot almost 3 years
    bg variable is not initialized in your example.