如何使用 SVG 文件在一个材料 UI 的 SvgIcon

我有一个 SVG 文件,我想用它来创建一个 SvgIcon 组件,我该怎么做呢?

文件中,所有的例子都使用预定义的材质图标或者一个奇怪的 <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />符号,我不知道它是什么!

105134 次浏览

<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/)

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)

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

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" />

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

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.

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.

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>
);
}
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

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

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.

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 />