How do I reference a local svg in React?

Viewed 10812

The following code compiles just fine but the icon doesn't show up:

enter image description here

Icon.jsx component:

import React from 'react';
import { css } from 'emotion';

import ArrowRight from "./arrow-right.svg";

export const iconTypes = {
    arrowRight: 'ARROW_RIGHT',
   // arrowLeft: 'ARROW_LEFT',
}

const iconSrc = {
    ARROW_RIGHT: ArrowRight,
   // ARROW_LEFT: ArrowLeft,
}


export default function Icon({ type }) {
    return (
        <div>
            <img src={iconSrc[type]}/>
        </div>
    )
};

In the browser the request looks like this:

 http://localhost:9001/%22data:image/svg+xml,%3Csvg%20version='1.1'%20xmlns='http://www.w3.org/2000/svg'%20width='512'%20height='512'%20viewBox='0%200%20512%20512'%3E%20%3Ctitle%3E%3C/title%3E%20%3Cg%20id='icomoon-ignore'%3E%20%3C/g%3E%20%3Cpath%20d='M310.627%20438.627l160-160c12.497-12.496%2012.497-32.758%200-45.255l-160-160c-12.497-12.496-32.758-12.496-45.255%200s-12.497%2032.758%200%2045.255l105.373%20105.373h-306.745c-17.673%200-32%2014.327-32%2032s14.327%2032%2032%2032h306.745l-105.373%20105.373c-6.248%206.248-9.372%2014.438-9.372%2022.627s3.124%2016.379%209.372%2022.627c12.497%2012.497%2032.758%2012.497%2045.255%200z'%3E%3C/path%3E%20%3C/svg%3E%22 

Icon.jsx story:

import { action } from "@storybook/addon-actions"; 
import Icon from "../components/Icon/Index"; 
import { iconTypes } from "../components/Icon/Index";


    storiesOf("Icon", module)
        .add("with text", () => (
            <Icon type={iconTypes.arrowRight}>
            </Icon>
        ));

When I fill the src with a http path (for ex: "https://s.cdpn.io/3/kiwi.svg") it works just fine.

When I open the svg file directly in the browser it also opens fine.

Any help would be appreciated.

6 Answers

To whoever comes here late as i did, i strongly suggest using react-svg

A sample code for it:

import SvgLogo from './logo.svg';
import ReactSVG from 'react-svg';

...

render(){
  return(
    <ReactSVG src={SvgLogo} />
  );
}

It has all types of customizations required, and great documentations and live examples.

Hope it helps!

It is much better to use the svg inline rather than as image. It is also easier to parameterise (e.g. the size):

Icon.jsx

import React from 'react';

export const iconTypes = {
  arrowRight: 'ARROW_RIGHT',
  arrowLeft: 'ARROW_LEFT',
}

export default function Icon({ type = iconTypes.arrowRight, size = 512 }) {
  return (
    <svg
      width={size}
      height={size}
      viewBox='0 0 512 512'>
      {type === iconTypes.arrowRight &&
        <path d='M310.627 438.627l160-160c12.497-12.496 12.497-32.758 0-45.255l-160-160c-12.497-12.496-32.758-12.496-45.255 0s-12.497 32.758 0 45.255l105.373 105.373h-306.745c-17.673 0-32 14.327-32 32s14.327 32 32 32h306.745l-105.373 105.373c-6.248 6.248-9.372 14.438-9.372 22.627s3.124 16.379 9.372 22.627c12.497 12.497 32.758 12.497 45.255 0z' />
      }
      {type === iconTypes.arrowLeft &&
        <path d="..." />
      }
    </svg>
  )
};

Note: you should leave the viewBox prop hardcoded 512 to make the scale work properly.

I think the best way to show default icon in your application is sprite them and use them with their class name. With that, you can cache and have a better performance.

<Icon name="plus" size={10} />

there are many free tools to sprite your image.

Can you use the SVG as a background style on a class and just switch them out depending on what icon you need?

For good performance you can just base64 encode the SVG and paste the contents in a background style in your CSS. http://b64.io/ this is a good site for encoding SVGs

E.g.

.kiwi {
  background-image: url("data:image/svg+xml;base64,PD94b...");
}

Any questions, shout!

This may be because of webpack file-loader options. Add outputPath to file-loader option and change publicPath to / in output object.

const path = require('path');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: { main: ['./ClientApp/boot.jsx'] },
  resolve: {
    extensions: ['.js', '.jsx'],
  },
  output: {
    path: path.join(__dirname, './wwwroot/dist'),
    filename: 'bundle.js',
    publicPath: '/',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)?$/,
        include: /ClientApp/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/react'],
            plugins: [require('@babel/plugin-proposal-class-properties')],
          },
        },
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              limit: 8192,
              outputPath: '/',
            },
          },
        ],
      },
    ],
  },
};

You need to configure webpack to load inline-svg, in order to do that, install the library svg-inline-loader from npm and add the following in your webpack config:

  module: {
    rules: [
      {
       //All the other rules
      },
      {
        test: /\.svg$/,
        loader: 'svg-inline-loader',
      },
    ],
  },

Hope this helps.

Related