how to set up an inline svg with webpack
Asked Answered
A

10

60

I am wondering how to set up an inline svg with webpack?

I am following the react-webpack-cookbook.

I have my webpack.config set up correctly with the file loader.

However the example shows using a background image like this:

.icon {
   background-image: url(./logo.svg);
}

which works fine, but I want to have an inline svg image how do I do this to include my logo.svg inline in my react component?

import React, { Component } from 'react'

class Header extends Component {

  render() {
    return (
        <div className='header'>
            <img src={'./logo.svg'} />
        </div>
    );
  }
};

export default Header
Aloin answered 13/12, 2015 at 23:45 Comment(0)
P
40

Here is a simple non-react solution.

  1. Install Svg inline loader
  2. In webpack.config.js add { test: /\.svg$/, loader: 'svg-inline-loader' }
  3. In your js file import svg image and add it to a DOM element like so
  import Svg from './svg.svg';

  function component() {
    const element = document.createElement('div');

    element.innerHTML = Svg;

    return element;
  }

  document.body.appendChild(component());
Prism answered 8/5, 2019 at 22:32 Comment(1)
kudos for not relying on react and presenting vanilla ECMAScriptBrandiebrandise
A
36

Actually Michelle's answer pointed me in the right direction, and that works nicely for loading an svg file with webpack and using it as your <img> src

However to actually get the inline svg, I needed to do the following:

Instead of file-loader use svg-inline-loader as your svg loader:

{ test: /\.svg$/, loader: 'svg-inline-loader' }

Then to load the svg inline in a component:

import React, { Component } from 'react'
import logo from "./logo.svg";

class Header extends Component {

  render() {
    return (
        <div className='header'>
          <span dangerouslySetInnerHTML={{__html: logo}} />
        </div>
    );
  }
};

export default Header

It looks like there is an inline svg wrapper for react svg-inline-react which would be another option instead of the <div dangerouslySetInnerHTML={{__html: mySvg}} />

Aloin answered 14/12, 2015 at 0:12 Comment(2)
just a note, this does not work at all with server rendering... so is not as useful as I once thought. I have gone back to using an svg as a background image. It does also seem to bloat the JavaScript bundle as well like inline svg's do, of course this would depend on how optimized the svgs are.Aloin
There is a good case study posted by Twitter which shows up why you should not use 'dangerouslySetInnerHTML' for displaying SVGs: medium.com/@paularmstrong/…Illstarred
A
33

I hope my late answer will still be useful for someone, because I don't like any of abovementioned options.

The react-svg-loader webpack loader allows you to import SVG icons like JSX components:

import Logo from './logo.svg';

class App extends Component {
  render() {
    return (
      <div className="App">
          <Logo fill="red" className="logo" width={50} height={50} />
      </div>
    );
  }
}

and minimum config looks like this:

{
  test: /\.svg$/,
  use: [
    {
      loader: "babel-loader"
    },
    {
      loader: "react-svg-loader",
      options: {
        jsx: true // true outputs JSX tags
      }
    }
  ]
}

The best part is that it just outputs the svg file contents, without any extra wrappers and dangerouslySetInnerHTML in your code.

Albertson answered 28/3, 2018 at 18:24 Comment(1)
This works perfect with latest React 16.10 and Webpack 4.41. Thanks!Cloth
C
21

Old question, but I didn't see this solution anywhere so I decided to post it, hoping it will help someone.

If you want to be able to style those SVG icons, you might want to load them with the raw loader:

webpack.config.js:

 { 
      test: /\.svg$/, 
      loader: 'raw-loader' 
 } 

The import in my view:

import closeIcon from 'svg/ic_close_black_24px.svg'; 

The template (Mustache uses 3 brackets to insert the SVG data (URL)unencoded):

<button id="closeModal">
  {{{closeIcon}}}
</button>

this way the SVG data will be inserted instead of the brackets and look like this:

<button id="closeModal">
  <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
    <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
    <path d="M0 0h24v24H0z" fill="none"></path>
  </svg>
</button>

I'm using Backbone with Mustache template engine with Webpack 2.5.1

Claypool answered 8/5, 2017 at 15:19 Comment(1)
Going to work with a string-template based library like backbone + mustache or vue, but not with react because it uses JSX instead of raw strings. Probably does work with "dangerouslySetInnerHTML" though...Eatables
K
12

If I'm not mistaken, since you're using the file loader, you can utilize it in much the same way as any other require. Webpack will turn require("./logo.svg") into a path to a file, which it will emit when it bundles.

import React, { Component } from 'react'

import mySvg from './logo.svg'

class Header extends Component {

  render() {
    return (
        <div className='header'>
            <img src={mySvg} />
        </div>
    );
  }
};

export default Header
Kamasutra answered 13/12, 2015 at 23:49 Comment(3)
It doesn't work for me :( could you post your webpack.config.js? Maybe I can spot my mistake there.Vagabond
Worked for me only after I restarted npm startWaine
and this adds the svg inline? or does it wrap the svg within an image tag?Brandiebrandise
R
6

Similar to another answer using React, there is also a handy Vue plugin as well.

vue-svg-loader just throw it in your configuration and start using. The nice thing is it will also run your svg through SVGO to optimize it.

Configuration

{
    test: /\.svg$/,
  loader: 'vue-svg-loader', // `vue-svg` for webpack 1.x
  options: {
    // optional [svgo](https://github.com/svg/svgo) options
    svgo: {
      plugins: [
        {removeDoctype: true},
        {removeComments: true}
      ]
    }
  }
}

Usage

<template>
  <nav id="menu">
    <a href="...">
      <SomeIcon class="icon" />
      Some page
    </a>
  </nav>
</template>

<script>
import SomeIcon from './assets/some-icon.svg';

export default {
  name: 'menu',
  components: {
    SomeIcon,
  },
};
</script>
Relume answered 26/4, 2018 at 9:8 Comment(0)
N
1

Angular Solution (2019): Use svg-sprite-loader to combine SVGs into a single sprite that's lazy-loaded with your Webpack bundles.

Webpack

{
  test: /\.svg$/,
  use: [
    'svg-sprite-loader',
    'svgo-loader' // Optimize SVGs (optional)
  ]
}

HTML

<svg>
    <use xlink:href="#arrow"/>
</svg>

Angular Component

export * from 'assets/images/icons/arrow.svg';

I use export (instead of import) to prevent the AOT compiler from removing the import during tree-shaking, while allowing for minimal code in the component, but you can use import if you prefer.

To use export in this way, you must configure the compiler to expect side effects from SVG files in package.json (i.e. you can not use "sideEffects": false). See the Webpack Tree Shaking Guide

"sideEffects": [
    "*.svg",
],
Nearsighted answered 1/10, 2019 at 1:27 Comment(0)
A
1

@svgr/webpack (npm) is the inline svg loader that create-react-app uses.

Add the rule to your webpack config:

{
  test: /\.svg$/,
  use: ['@svgr/webpack'],
}

Then you can import svgs as React components:

import Star from './star.svg'

const App = () => (
  <div>
    <Star />
  </div>
)
Awful answered 10/5, 2020 at 14:28 Comment(0)
M
1

Folks who use svg-inline-loader and who stuck with "Cannot find module" error try to install babel-plugin-inline-react-svg and add it to the babel plugins:

"plugins": [
    ...
    ["inline-react-svg", {}]
],
...
Madonnamadora answered 8/9, 2020 at 7:29 Comment(0)
J
0

Old question but still relevant when strings are needed from SVG assets. With webpack > 5 source assets are the way to do this simply.

If using a loader like SVGR to get some SVG's imported as React components, you can still get source assets. Use the oneOf rule for this. Edit webpack.config.js thus:

   module: {
      rules: [
        {
          test: /\.svg$/,
          oneOf: [
            {
              issuer: /\.[jt]sx?$/,
              resourceQuery: /react/, // *.svg?react to get a component
              use: ["@svgr/webpack", "url-loader"],
            },
            {
              type: "asset/source",
              parser: {
                dataUrlCondition: {
                  maxSize: 200,
                },
              },
            },
          ],
        },
        // ...
     ]
   }

Note that when assets are imported they come in as a "module" that just has text in it. It can be dynamically/async imported as needed like this:

    const { default: image } = await import("/assets/myimage.svg");

Now the variable image will have the SVG text content in it.

    console.log(image);

Result:

<?xml version="1.0" encoding="utf-8"?>
   <svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
     viewBox="0 0 500 275">
    <g>
        <circle class="st0" cx="423.536" cy="192.862" r="76.464"/>
    </g>
</svg>

Jacintajacinth answered 10/7 at 0:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.