Eui: [EuiIcon] Support straight SVG usages

Created on 30 Jan 2020  路  14Comments  路  Source: elastic/eui

Currently, when using custom icons with EuiIcon and passing an .svg file to the type prop, EuiIcon always spits out an img tag with the SVG data-uri as the src. This causes issues when consumers want to use the SVG content directly, for instance, when coloring the fills based on the theme/content with fill: currentColor.

Current example:

import reactSvg from '../../images/custom.svg';
<EuiIcon type={reactSvg} size="xl" />

Renders:

<img src="data:image/svg+xml;base64,{...SVGcontent}" class="euiIcon euiIcon--xLarge euiIcon-isLoaded">

What needs to render:

<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" class="euiIcon euiIcon--xLarge euiIcon-isLoaded" focusable="false" role="img" aria-hidden="true">
  ...SVG content...
</svg>

cc @gjones @thompsongl

engineer icons skip-stale-check

Most helpful comment

Hi @manishmodi02,

The way to integrate is to do what @chandlerprall recommended:

the common practice is to rely on the loaders configuration to import content into whatever desired format.

The best way, in my opinion, is to transform your SVG into a React Component. For that, you can use in your project a babel plugin like babel-plugin-inline-react-svg or use webpack and @svgr/webpack.

Or you can do that manually and transform your SVG into a react component using an online editor: svg2jsx.

Then you just need to import the component and:

<EuiIcon type={SvgReactComp} size="xl" title="SVG React Component" />

In the following example I'm importing an SVG directly as React component. In the background there is a @svgr/webpack loader part of the create-react-app.

So when I import { ReactComponent as Logo } from './logo.svg'; the svg is transformed intro a react component:

https://codesandbox.io/s/sweet-black-3gi2b?file=/index.js

All 14 comments

This depends on the consuming application, not EUI. The application decides how imported svgs behave, e.g. as an image (file-loader, image-loader, etc) or as a React component (via another webpack loader, airbnb's babel plugin, etc).

In your example code, if you were to console.log(reactSvg) you'll see it isn't a React component, likely it is a is either a data: or http url.

How do you suspect that a consuming application get the end result needed (inline SVG content)?

They would need something added to their build/bundle pipeline to convert svgs to React components (a webpack loader, the babel plugin, etc). These are common in build configurations and easily discoverable - but we probably should update the docs to push people in the direction that they should discover one.

@thompsongl And I talked a bit about this one last week when it came up via Cloud. Do you have any thoughts on this?

I worry that @chandlerprall's solution creates a whole bunch of hoops for a simple inline SVG.

@chandlerprall is right that processing an SVG import would be entirely on the consumer to handle via webpack, etc. Assuming that's understood, though, the desire is still for EuiIcon to accept an SVG as a string (if they bypass import altogether) or React component and render it as an <svg /> instead of an <img />.

What might be more appropriate is a new HOC that could provide consumers with a way to wrap their SVG and get rendered output like EuiIcon without going through EuiIcon itself.

Hii @cchaos, If no one else has tried it I would like to implement the solution suggested by @thompsongl.

I'm not sure this is ready to be worked on, we haven't reached a consensus of what change(s) should be made.

the desire is still for EuiIcon to accept an SVG as a string (if they bypass import altogether) or React component and render it as an instead of an

SVG as string can be a little problematic, as we already accept strings to refer to external URLs. This could be used today to pass the SVG as a data URI. Otherwise, we have to introduce some heuristic to determine if the thing is injectable content and apply via dangerouslySetInnerHTML, which has security implications.

Passing "random" React components is already supported, though the typescript definition in EuiIcon is wrong (Element instead of Component).

Maybe it's best if I restate the goal:

Allow EuiIcon to render custom SVG content. Not as dataURI. Without needing to turn it into a React component first.

So that the final output would be:

<svg class="euiIcon" ...>
  <path />
</svg>

Which would allow the consumer to dynamically style the icon the same way we provide dynamic style with our basic glyphs.

I don't think this is something we'd want to do in EUI. It would require parsing the input string as HTML, and then either converting the parsed DOM structure to the proper React.createElement calls, or injecting the class & other attributes into the HTML string and render that into the DOM, which opens up security concerns.

Instead, When building React apps, the common practice is to rely on the loaders configuration to import content into whatever desired format. This gives the consumer the freedom to import their svgs as urls to the images, or data uris, or as React elements (e.g. via babel-plugin-inline-react-svg, which we use when pre-compiling our svgs to React components)

I tend to agree with Chandler here. I think possibly the takeaway is... The expectation is that they want will _likely_ want this functionality. We can at least hint in the docs at a way they could do it within their application. Right now they just assume it should work because of course you'd want it work that way.

Perhaps we close this issue and replace it with a docs one?

Hii @cchaos, If no one else has tried it I would like to implement the solution suggested by @thompsongl.

Any Success friend?

Hi Caroline,

So does that mean that there is no way that we can integrate Custom SVGs to EUI ICON?

Hi @manishmodi02,

The way to integrate is to do what @chandlerprall recommended:

the common practice is to rely on the loaders configuration to import content into whatever desired format.

The best way, in my opinion, is to transform your SVG into a React Component. For that, you can use in your project a babel plugin like babel-plugin-inline-react-svg or use webpack and @svgr/webpack.

Or you can do that manually and transform your SVG into a react component using an online editor: svg2jsx.

Then you just need to import the component and:

<EuiIcon type={SvgReactComp} size="xl" title="SVG React Component" />

In the following example I'm importing an SVG directly as React component. In the background there is a @svgr/webpack loader part of the create-react-app.

So when I import { ReactComponent as Logo } from './logo.svg'; the svg is transformed intro a react component:

https://codesandbox.io/s/sweet-black-3gi2b?file=/index.js

馃憢 Hey there. This issue hasn't had any activity for 180 days. We'll automatically close it if that trend continues for another week. If you feel this issue is still valid and needs attention please let us know with a comment.

Was this page helpful?
0 / 5 - 0 ratings