87

I've got an SVG file and I want to make an SvgIcon component out of it, how should I do that?

In the documentation, all the examples use either predefined Material Icons or a strange notation of <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" /> which I have no idea what it is!

Mehran
  • 15,593
  • 27
  • 122
  • 221

13 Answers13

54

1. Using <Icon/> component and an <img/> element

To use a SVG file as an icon, I used the <Icon/> component with an <img/> element inside, setting the height: 100% to the img element and textAlign: center to the root class of the <Icon/> component did the trick:

JSX:

import Icon from '@material-ui/core/Icon';
import { makeStyles } from '@material-ui/styles';
...

<Icon classes={{root: classes.iconRoot}}>
  <img className={classes.imageIcon} src="/graphics/firebase-logo.svg"/>
</Icon>

Styles:

const useStyles = makeStyles({
  imageIcon: {
    height: '100%'
  },
  iconRoot: {
    textAlign: 'center'
  }
});

Result:

Material UI Drawer Icon from SVG

UPDATE

As Lorenz Haase mentions in the comments, there is a slighly cuttting of the SVG at the bottom, which it can be fixed if we use flexbox and inherit width and height:

const useStyles = makeStyles({
  imageIcon: {
    display: 'flex',
    height: 'inherit',
    width: 'inherit'
  }
});

2. Using <SvgIcon/> component and @svgr/webpack webpack loader

According to the official MUI documentation, we can use <SvgIcon/> component props and have a @svgr/webpack loader to load .svg files using ESM imports.

Component prop

You can use the SvgIcon wrapper even if your icons are saved in the .svg format. svgr has loaders to import SVG files and use them as React components. For example, with webpack:

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

// ---
import StarIcon from './star.svg';

<SvgIcon component={StarIcon} viewBox="0 0 600 476.6" />

It's also possible to use it with "url-loader" or "file-loader". It's the approach used by Create React App.

// webpack.config.js
{
  test: /\.svg$/,
  use: ['@svgr/webpack', 'url-loader'],
}

// ---
import { ReactComponent as StarIcon } from './star.svg';

<SvgIcon component={StarIcon} viewBox="0 0 600 476.6" />
Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
  • 1
    I attempted this soloution, although it works I could not style it with a different colour, seems impossible – Max Carroll Nov 07 '19 at 13:53
  • @MaxCarroll how would you style a SVG file in general not using material-ui? I don't think it can happen, it doesn't work that way. Check [img-src-svg-changing-the-styles-with-css](https://stackoverflow.com/questions/24933430/img-src-svg-changing-the-styles-with-css) for more details. – Christos Lytras Nov 07 '19 at 15:41
  • 1
    I have since discovered its because the svg had fill colours hard coded in the svg, with a little editing of the svg it worked. You can style a svg using css using the properties stroke and fill. – Max Carroll Nov 07 '19 at 15:50
  • 1
    This is a great solution but could be optimized. As you can see in the image provided with the answer, the firebase logo gets cut off at the bottom (should look like this: https://firebase.google.com/brand-guidelines) Adjusting the styles fixes this problem: ```const useStyles = makeStyles({ imageIcon: { display: 'flex', height: 'inherit', width: 'inherit', } });``` The image won't get cut off. The root style isnt necessary anymore and the image gets its dimensions from the parent Icon component. – Lorenz Haase Oct 21 '20 at 08:59
  • @LorenzHaase I didn't notice that it gets cut at the bottom, nice catch. I'll check out the rules and I will update my answer, thank you. – Christos Lytras Oct 21 '20 at 09:50
  • Mthod 1 works but I cannot seem to change the color of the icon. Is that possible? – Rhubarb65 Sep 09 '21 at 11:33
  • Not sure why this is not marked as the "right answer" but it worked like a charm – GiselleMtnezS Apr 07 '22 at 03:31
  • @ChristosLytras I have added an answer below but you should probably edit your answer to explain why setting the viewBox is important. I don't really understand what is happening well enough to explain it clearly myself. Just putting your values doesn't work on an arbitrary svg. – AntonOfTheWoods Jul 16 '22 at 02:29
29

<path /> is an SVG path, i.e. the internal bits of the SVG. the SvgIcon component really should be able to take a path, but it doesn't :(

instead you can create a component like https://github.com/callemall/material-ui/blob/56c113217d7d05d8bb0712771b727df81984d04b/src/svg-icons/action/home.js

with your svg source in place of the path. (I recommend minifying it a bit using https://jakearchibald.github.io/svgomg/)

Gerbal
  • 661
  • 6
  • 8
  • the path is hard codec, cannot this be done by loading the svg like we do with png ? – AlainIb Jun 23 '20 at 10:15
  • 1
    i did not quite understand this answer :( i do not see the path to the svg file anywhere in this answer – Marquez Sep 17 '20 at 20:47
  • 1
    There are 2 types of path in this whole issue. There is the 1)path as in _where is the svg file located_ and then there is the 2) **SVG-specific** path that is a property of the SVG element that contains information on how to draw itself. – jusopi Sep 29 '21 at 19:41
26

You can import SVGs directly as React components and use them in <SvgIcon>:

import React from "react";
import { ReactComponent as Logo } from "./logo.svg";
import SvgIcon from "@material-ui/core/SvgIcon";

const App = () => (
  <SvgIcon>
    <Logo />
  </SvgIcon>
);

export default App;

See also: https://create-react-app.dev/docs/adding-images-fonts-and-files/#adding-svgs

Unfortunately React seems not able to render all kind of SVGs (e.g. modified with Inkscape, Illustrator) yet. But at least the default logo.svg inside a create-react-app project works.

neumann
  • 1,105
  • 10
  • 10
  • 1
    Thanks a lot! Your answer worked for me! Guess this is familiar to you, but if somebody wants to use InkScape or Illustrator svgs with React, you can do this using the https://github.com/svg/svgo client. – rickstaa Jan 14 '21 at 12:46
8

To obtain the path for SvgIcon, open svg file with the text editor and copy the corresponding path expression.

Eugene Tulika
  • 703
  • 5
  • 11
5

The SvgIcon is not meant for this purpose. More on this @ Github.

You are probably looking for this startIcon={<img src={google}></img>}

import google from "../../Assets/img/google.svg";
import GitHubIcon from "@material-ui/icons/GitHub";
const useStyles = makeStyles((theme) => ({
         root: {
                display: "flex",
                flexDirection: "column",
                margin: theme.spacing(1),
                },
          button: {
               margin: "0.5rem",
                  },
          googleStyle: {
                fillColor: theme.palette.primary.main,
                    },
              }));

export default function ContainedButtons() {
  const classes = useStyles();
   return (
  <div>
  <Button
    variant="contained"
    color="secondary"
    className={classes.button}
    startIcon={<img src={google}></img>}
  >
    Login With Google
  </Button>
  <Button
    variant="contained"
    color="secondary"
    className={classes.button}
    startIcon={<GitHubIcon />}
  >
    Login with GitHub
  </Button>
</div>
);
}
Aneesh Panoli
  • 206
  • 3
  • 7
  • This is probably the correct answer. Definitely should be higher up. Worked for me. Thanks! – Leon May 18 '21 at 17:18
4

For Vite users

Install vite-plugin-svgr

npm i -D vite-plugin-svgr

In vite.config.ts

import svgr from 'vite-plugin-svgr'

export default {
  // ...
  plugins: [svgr()],
}

Add a declaration in src/vite-env.d.ts if you use typescript.

/// <reference types="vite-plugin-svgr/client" />

Now you can use your SVG as

import { ReactComponent as Logo } from './logo.svg'

// ...
<SvgIcon component={Logo} inheritViewBox />
secretshardul
  • 1,635
  • 19
  • 31
3

The solution that worked for me is the following

 import React from 'react';
 import pure from 'recompose/pure';
 import {SvgIcon} from '@material-ui/core';

 let smile = (props) => (
   <SvgIcon {...props} >
<path d="M256,32C132.281,32,32,132.281,32,256s100.281,224,224,224s224-100.281,224-224S379.719,32,256,32z M256,448
c-105.875,0-192-86.125-192-192S150.125,64,256,64s192,86.125,192,192S361.875,448,256,448z M160,192c0-26.5,14.313-48,32-48
s32,21.5,32,48c0,26.531-14.313,48-32,48S160,218.531,160,192z M288,192c0-26.5,14.313-48,32-48s32,21.5,32,48
c0,26.531-14.313,48-32,48S288,218.531,288,192z M384,288c-16.594,56.875-68.75,96-128,96c-59.266,0-111.406-39.125-128-96"/>
   </SvgIcon>
 );

 smile = pure(smile);
 smile.displayName = 'smile';     
 smile.muiName = 'SvgIcon';

 export default smile;

Check this example of material ui icon

Jorge Santos
  • 397
  • 4
  • 3
2

If making more than one icon, you may not want to repeat all the boilerplate in the example referenced in the accepted answer. You can use a wrapper component generator like:

const wrapSvgPath = (path, viewBox='0 0 24 24') => (props) => (
    <SvgIcon {...props} viewBox={viewBox}>{path}</SvgIcon>
)

used like:

const facebookPath = (<path
        d="M17,2V2H17V6H15C14.31,6 14,6.81 14,7.5V10H14L17,10V14H14V22H10V14H7V10H10V6A4,4 0 0,1 14,2H17Z" />
)
export const FacebookIcon = wrapSvgPath(facebookPath)
Ed Staub
  • 15,480
  • 3
  • 61
  • 91
1

Open your svg file in a editor (example: vscode). You will see a file structure of something similar like this ->

<svg width="27" height="28" viewBox="0 0 27 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.91942 27.5138L6.15827 26.984L8.69599 21.1562H8.68852L13.5027 10.3812L18.3542 21.1562H18.3467L21.1083 27.5138H26.2509L15.8687 4.72523L13.5698 0L0.933594 27.5138H5.91942Z" fill="#A7A9AC"/>
</svg>

Then go to your react components, replace path with your own path. This should work. Add styles for custom change of design.

Saikat halder
  • 548
  • 4
  • 11
1
import SvgIcon from '@material-ui/core/SvgIcon'
import Register from "./../../media/register.svg"
import React from 'react';

const getSvgIconMaterial = (Icon: string, props?: any) => {
    return (
        <SvgIcon component="object">
            <embed type="image/svg+xml" src={Icon} style={{ height: "100%" }} />
        </SvgIcon>
    );
};

use like this

<>{getSvgIconMaterial(Register)}</>

Probably not perfect but works nicely. Maybe add a small padding/margin. React SVG Custom Component

ru4ert
  • 998
  • 2
  • 14
  • 25
1

To add to Christos' answer, make sure the viewBox is set to the dimensions that mui is giving the svg element or you might get the object clipped.

For example, when trying to put one of the Discord svgs into an IconButton, I was getting:

<svg width="71" height="55"...

But until I set the viewBox properly, was only getting the top left corner of the icon actually visible. After setting:

<SvgIcon component={DiscordIcon} viewBox="0 0 71 55" />

That then meant everything was visible and adapting the size of the IconButton worked as expected.

AntonOfTheWoods
  • 809
  • 13
  • 17
0

import svg file to your react application folder . Then in the file / component in which you want to use the svg icon use

import { ReactComponent as MyIcon } from '../svgiconfolder/myicon.svg'

export function MyCompnent(){return <MyIcon />}

if you want to change colour use

import {ReactComponent as MyIcon} from '../svgiconfolder/myicon.svg'

export function MyCompnent(){return <MyIcon fill="#00000"/>}//use the color code in fill or if there is any color attribute in svg file try to use that
Vijay
  • 151
  • 1
  • 6
0

Pull the image into your button file using import. Use an Avatar to render the image and then put that in the Button.

import logo from "../assets/logoWhite.svg";

const title = "Click me"

<Button>
       <Avatar src={logo} alt="logo" sx={{ width: 24, height: 24 }} />
       {title}
</Button>
Tristan Newman
  • 357
  • 1
  • 4
  • 13