SVG sprite images not showing on iOS & Safari using React - reactjs

I am trying to use my svg images with a sprite file. Importing sprite file in relevant component and using custom Svg component to render images with symbol id's. Svg image dimensions described in css classes. Everything works without any problems in Firefox and Chrome but mobile & desktop Safari does not rendering images. Based on suggestions, tried xmlns:xlink, width, height, viewBox attributes on svg tag. Nothing worked.
In source code I can see that SVG is present and it's taking space in page. But it's blank.
I appreciate any suggestions.
Here is my sprite file content:
<svg>
<symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 8.586" id="icon-angle-left">
<g>
<g fill="none">
<path d="M0 0H6.981V3.491H0z" transform="translate(0.472 0.707) translate(3.821 3) rotate(90) translate(-2.809 0.331)"></path>
<path stroke="#555" stroke-linecap="round" d="M2577 1784.713l3.586 3.586 3.586-3.586" transform="translate(0.472 0.707) translate(3.821 3) rotate(90) translate(-2579.998 -1784.713)"></path>
</g>
</g>
</symbol>
<symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21" id="icon-home">
<path fill="none" d="M0 0h21v21H0z"></path>
<path fill="#555" d="M10.75 5.354l4.375 3.938v6.834h-1.75v-5.25h-5.25v5.25h-1.75V9.291l4.375-3.937m0-2.354L2 10.875h2.625v7h5.25v-5.25h1.75v5.25h5.25v-7H19.5z" transform="translate(-0.25 -0.375)"></path>
</symbol>
</svg>
Here is my Svg component:
import React from 'react';
import './style.scss';
const Svg = ({ symbolId, className }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" className={className}>
<use xlinkHref={symbolId}></use>
</svg>
);
};
export default Svg;
And using it like this;
import React from 'react';
import sprite from 'sprites/product-list.svg';
import Svg from 'shared/Svg';
const MyComponent = () => {
return (
<div>
<Svg className="image" symbolId={sprite + "#icon-home"} />
</div>
);
};
export default MyComponent;

It turns out that Safari does not support working with external svg files like this.
This is how we solved this situation:
Created a icons.jsx file that exports all the svg contents as a component. And used a prop for class name for further styling:
import React from 'react';
export const IconTick = ({ className }) => (
<svg className={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16.662 12.505" id="icon-tick">
<path d="M14.151.431L5.629 8.953 2.511 5.835a1.471 1.471 0 0 0-2.08 2.08l4.159 4.158a1.469 1.469 0 0 0 2.08 0l9.562-9.562a1.471 1.471 0 0 0-2.081-2.08z" />
</svg>
);
export const IconTimes = ({ className }) => (
<svg className={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" id="icon-times">
<g id="Component_139_1" data-name="Component 139 – 1" transform="translate(1.414 1.414)">
<path id="Path_866" data-name="Path 866" d="m46.167 46.5-6 6 6 6" transform="translate(-34.167 -46.5)" />
<path id="Path_1015" data-name="Path 1015" d="m40.167 46.5 6 6-6 6" transform="translate(-40.167 -46.5)" />
</g>
</svg>
);
Created an alias in webpack.common.js for accessing svg's with a clean way:
module.exports = {
...
resolve: {
alias: {
...
icons: path.resolve(__dirname, 'src/SharedComponents/Icons/'),
...
},
...
},
...
};
And just used them like:
import React from 'react';
import { IconAngleLeft, IconTick } from 'icons';
const MyOtherComponent = () => {
return (
<div className="some-div">
<IconAngleLeft className={'foo'} />
<IconTick className={'bar'} />
</div>
);
};
export default MyOtherComponent;
It works everywhere without any problems and it's very easy I think. But you may need to correct your svg contents for Jsx syntax.

Related

How to render an SVG on the DOM in react 18?

I have seen so many people discussing stuff that doesn't work all the times.
I am using react 18. I want to import an SVG file on my disk and render it on my DOM.
I tried to import it as:
import { ReactComponent as Underconstruction } from '../../media/business/under-construction.svg';
const Sidebar= ()=> {
return <Underconstruction />
}
But it showed me this error:
I have also tried to use it as a regular image tag and it showed me the same error:
const Sidebar= ()=> {
return <img src={require('../../media/business/under-construction.svg')} />
}
and I have tried to use require() but it showed 404 couldn't find the file:
const Sidebar= ()=> {
return <img src="../../media/business/under-construction.svg" />
}
I am using create-react-app
I am using react 18.
20 Oct 2022.
The easiest way to use your svg as a React component. So you do not need to care about the config
import React from 'react'
export function YourFileName() {
return (
<svg
...
>
<path
...
...
/>
</svg>
)
}
You can get svg tag by open svg by your code editor like vscode
Here are a couple of options for importing SVG into React:
Importing SVG in react as an image tag
Import SVG in react as JSX
Importing SVG as react component (Inline SVGs)
Importing SVGs in React Image Tag
import myLogo from '/path/to/image.svg'
const Button = () => {
return (
<button>
<img src={myLogo} alt="SVG logo image"/>
</button>
);
}
Importing SVG in React as JSX
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11 19l-7-7 7-7m8 14l-7-7 7-7"
/>
</svg>
Importing SVGs as React Component
export const LeftArrow = () =>{
return(
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M11 19l-7-7 7-7m8 14l-7-7 7-7"
/>
</svg>
)
}
Now, you can import and render the react SVG component in another component like this:
import { LeftArrow } from './path/to/LeftArrow.jsx'
export const Button = () =>{
return(
<button>
<LeftArrow />
</button>
)
}
The easiest way is putting your svg in public folder like this
Then you can use it directly using path, without import as component:
<img src="/media/business/under-construction.svg" />
Hope it help!

Why is the SvgIcon is not rendering in React MUI v5

I am trying to create a custom SVG component with material UI and typescript in rect that can be reused as an icon anywhere.
But, the icon is not rendering in the screen. I can't figure out why?
import { SvgIcon, SvgIconProps } from "#mui/material";
const ImgCompress: React.FunctionComponent<SvgIconProps> = (props) => {
return (
<SvgIcon
viewBox="0 0 512.000000 512.000000"
width="512.000000pt"
height="512.00000pt"
transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
stroke="none"
fill="#000000"
preserveAspectRatio="xMidYMid meet"
>
<path d="M15,19.16V15.07a4.27,4.27,0,0,0,6,0h0a4.27,4.27,0,0,0,0-6h0a4.27,4.27,0,0,0-6,0l-3,3-3,3a4.27,4.27,0,0,1-6,0h0a4.27,4.27,0,0,1,0-6h0A4.27,4.27,0,0,1,9,9" />
</SvgIcon>
);
};
export default ImgCompress;

SVG in React Component [duplicate]

This question already has answers here:
How to display svg icons(.svg files) in UI using React Component?
(16 answers)
Closed 12 months ago.
i've downloaded an svg image which i was intending in using it as a react component , but i paste the svg code to the functional component i get error markes within the tags , what i can do to fix this so ill be able to use the icon
import React from 'react'
const CardanoIcon = () => {
return (
<svg height="256" viewBox="0 0 256 256" width="256" xmlns="http://www.w3.org/2000/svg"><defs><style>
.cls-1 {
fill: #3468d1;
}
.cls-2 {
fill: #fff;
fill-rule: evenodd;
}
</style></defs><g data-name="cardano ada" id="cardano_ada"><g data-name="cardano ada" id="cardano_ada-2"><circle class="cls-1" cx="128" cy="128" data-name="Эллипс 6" id="Эллипс_6" r="128"/><path class="cls-2" d="M265.727,717.735a12.652,12.652,0,1,1-12.654,12.652A12.654,12.654,0,0,1,265.727,717.735Zm30.546,0a12.652,12.652,0,1,1-12.655,12.652A12.654,12.654,0,0,1,296.273,717.735Zm0,53.226a12.652,12.652,0,1,1-12.655,12.652A12.653,12.653,0,0,1,296.273,770.961Zm-30.546,0a12.652,12.652,0,1,1-12.654,12.652A12.653,12.653,0,0,1,265.727,770.961Zm-15.709-26.177a12.652,12.652,0,1,1-12.654,12.652A12.654,12.654,0,0,1,250.018,744.784Zm61.964,0a12.652,12.652,0,1,1-12.655,12.652A12.653,12.653,0,0,1,311.982,744.784Zm14.4-21.813a7.853,7.853,0,1,1-7.855,7.853A7.853,7.853,0,0,1,326.382,722.971Zm0,52.352a7.853,7.853,0,1,1-7.855,7.853A7.853,7.853,0,0,1,326.382,775.323Zm-90.764,0a7.853,7.853,0,1,1-7.854,7.853A7.854,7.854,0,0,1,235.618,775.323Zm0-52.352a7.853,7.853,0,1,1-7.854,7.853A7.854,7.854,0,0,1,235.618,722.971ZM281,696.794a7.853,7.853,0,1,1-7.855,7.853A7.853,7.853,0,0,1,281,696.794ZM281,801.5a7.853,7.853,0,1,1-7.855,7.853A7.853,7.853,0,0,1,281,801.5Zm33.6,7.853a6.544,6.544,0,1,1-6.546,6.544A6.544,6.544,0,0,1,314.6,809.353Zm-67.2,0a6.544,6.544,0,1,1-6.546,6.544A6.544,6.544,0,0,1,247.4,809.353Zm0-117.794a6.544,6.544,0,1,1-6.546,6.544A6.544,6.544,0,0,1,247.4,691.559Zm67.2,0a6.544,6.544,0,1,1-6.546,6.544A6.544,6.544,0,0,1,314.6,691.559Zm34.036,58.461a6.544,6.544,0,1,1-6.545,6.544A6.545,6.545,0,0,1,348.636,750.02Zm-135.273,0a6.544,6.544,0,1,1-6.545,6.544A6.544,6.544,0,0,1,213.363,750.02Zm-4.8-40.138a5.236,5.236,0,1,1-5.236,5.235A5.236,5.236,0,0,1,208.563,709.882Zm0,83.765a5.236,5.236,0,1,1-5.236,5.235A5.236,5.236,0,0,1,208.563,793.647Zm144.873,0a5.236,5.236,0,1,1-5.236,5.235A5.236,5.236,0,0,1,353.436,793.647Zm0-83.765a5.236,5.236,0,1,1-5.236,5.235A5.236,5.236,0,0,1,353.436,709.882ZM281,668a5.236,5.236,0,1,1-5.237,5.235A5.235,5.235,0,0,1,281,668Zm0,167.529a5.236,5.236,0,1,1-5.237,5.236A5.236,5.236,0,0,1,281,835.529Zm46.254-3.49a4.363,4.363,0,1,1-4.363,4.363A4.363,4.363,0,0,1,327.254,832.039Zm-92.509,0a4.363,4.363,0,1,1-4.363,4.363A4.363,4.363,0,0,1,234.745,832.039Zm0-158.8a4.363,4.363,0,1,1-4.363,4.363A4.363,4.363,0,0,1,234.745,673.235Zm92.509,0a4.363,4.363,0,1,1-4.363,4.363A4.363,4.363,0,0,1,327.254,673.235Zm45.382,79.4A4.363,4.363,0,1,1,368.273,757,4.363,4.363,0,0,1,372.636,752.637Zm-183.273,0A4.363,4.363,0,1,1,185,757,4.363,4.363,0,0,1,189.363,752.637Z" data-name="Эллипс 6 копия 29" id="Эллипс_6_копия_29" transform="translate(-152 -629)"/></g></g></svg>
)
}
export default CardanoIcon
create a file like an icon.svg and paste the SVG you have downloaded to that file then you can import SVG file in your component like this:
import icon from './icon.svg'
const CustomIcon = () => {
return (
<img src={icon} alt="icon" />
)
}
export default CustomIcon;
If you are using CRA (Create React App) for your React environment, I would suggest pulling out the contents of the <style> tag out into a separate .css file and then importing it into the React component.
So, your CardanoIcon.css would look as follows:
.cls-1 {
fill: #3468d1;
}
.cls-2 {
fill: #fff;
fill-rule: evenodd;
}
And your React component would look as follows - note the .css import on line 2. Also, note that you need to change class to className
import React from 'react';
import './CardanoIcon.css';
const CardanoIcon = () => (
<svg
height="256"
viewBox="0 0 256 256"
width="256"
xmlns="http://www.w3.org/2000/svg"
>
<defs></defs>
<g data-name="cardano ada" id="cardano_ada">
<g data-name="cardano ada" id="cardano_ada-2">
<circle
className="cls-1"
cx="128"
cy="128"
data-name="Эллипс 6"
id="Эллипс_6"
r="128"
/>
<path
className="cls-2"
d="M265.727,717.735a12.652,12.652,0,1,1-12.654,12.652A12.654,12.654,0,0,1,265.727,717.735Zm30.546,0a12.652,12.652,0,1,1-12.655,12.652A12.654,12.654,0,0,1,296.273,717.735Zm0,53.226a12.652,12.652,0,1,1-12.655,12.652A12.653,12.653,0,0,1,296.273,770.961Zm-30.546,0a12.652,12.652,0,1,1-12.654,12.652A12.653,12.653,0,0,1,265.727,770.961Zm-15.709-26.177a12.652,12.652,0,1,1-12.654,12.652A12.654,12.654,0,0,1,250.018,744.784Zm61.964,0a12.652,12.652,0,1,1-12.655,12.652A12.653,12.653,0,0,1,311.982,744.784Zm14.4-21.813a7.853,7.853,0,1,1-7.855,7.853A7.853,7.853,0,0,1,326.382,722.971Zm0,52.352a7.853,7.853,0,1,1-7.855,7.853A7.853,7.853,0,0,1,326.382,775.323Zm-90.764,0a7.853,7.853,0,1,1-7.854,7.853A7.854,7.854,0,0,1,235.618,775.323Zm0-52.352a7.853,7.853,0,1,1-7.854,7.853A7.854,7.854,0,0,1,235.618,722.971ZM281,696.794a7.853,7.853,0,1,1-7.855,7.853A7.853,7.853,0,0,1,281,696.794ZM281,801.5a7.853,7.853,0,1,1-7.855,7.853A7.853,7.853,0,0,1,281,801.5Zm33.6,7.853a6.544,6.544,0,1,1-6.546,6.544A6.544,6.544,0,0,1,314.6,809.353Zm-67.2,0a6.544,6.544,0,1,1-6.546,6.544A6.544,6.544,0,0,1,247.4,809.353Zm0-117.794a6.544,6.544,0,1,1-6.546,6.544A6.544,6.544,0,0,1,247.4,691.559Zm67.2,0a6.544,6.544,0,1,1-6.546,6.544A6.544,6.544,0,0,1,314.6,691.559Zm34.036,58.461a6.544,6.544,0,1,1-6.545,6.544A6.545,6.545,0,0,1,348.636,750.02Zm-135.273,0a6.544,6.544,0,1,1-6.545,6.544A6.544,6.544,0,0,1,213.363,750.02Zm-4.8-40.138a5.236,5.236,0,1,1-5.236,5.235A5.236,5.236,0,0,1,208.563,709.882Zm0,83.765a5.236,5.236,0,1,1-5.236,5.235A5.236,5.236,0,0,1,208.563,793.647Zm144.873,0a5.236,5.236,0,1,1-5.236,5.235A5.236,5.236,0,0,1,353.436,793.647Zm0-83.765a5.236,5.236,0,1,1-5.236,5.235A5.236,5.236,0,0,1,353.436,709.882ZM281,668a5.236,5.236,0,1,1-5.237,5.235A5.235,5.235,0,0,1,281,668Zm0,167.529a5.236,5.236,0,1,1-5.237,5.236A5.236,5.236,0,0,1,281,835.529Zm46.254-3.49a4.363,4.363,0,1,1-4.363,4.363A4.363,4.363,0,0,1,327.254,832.039Zm-92.509,0a4.363,4.363,0,1,1-4.363,4.363A4.363,4.363,0,0,1,234.745,832.039Zm0-158.8a4.363,4.363,0,1,1-4.363,4.363A4.363,4.363,0,0,1,234.745,673.235Zm92.509,0a4.363,4.363,0,1,1-4.363,4.363A4.363,4.363,0,0,1,327.254,673.235Zm45.382,79.4A4.363,4.363,0,1,1,368.273,757,4.363,4.363,0,0,1,372.636,752.637Zm-183.273,0A4.363,4.363,0,1,1,185,757,4.363,4.363,0,0,1,189.363,752.637Z"
data-name="Эллипс 6 копия 29"
id="Эллипс_6_копия_29"
transform="translate(-152 -629)"
/>
</g>
</g>
</svg>
);
export default CardanoIcon;

The tag <ReactComponent> is unrecognized in this browser

I'm going some test with jest, I'm currently testing component which using svg. I'm using ReactComponent to display my svg as react suggest us to do.
When I'm launching my test I always have this message :
Warning: The tag is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter. at ReactComponent
So my code Look like this :
import React, { ReactElement, useState } from "react";
// SVG;
import { ReactComponent as Play } from "../../../../../../../../assets/svg/PLAY.svg";
export interface IComponentProps {
valueX: valueX;
color: string;
}
export default function Component({
valueX,
color,
}: IComponentProps): ReactElement {
const [state, setState] = useState<boolean>(false);
return (
<div className="wrapper-lesson-video">
<div className="display-colum">
<div
className="pickgradient"
style={{
background: `linear-gradient( to bottom, rgba(0, 0, 0, 0.5) 0%, ${color} 100% )`,
}}
>
<img
alt="title"
src="https://cdn.xxxx.com/photo/xxxx.jpg"
/>
</div>
<div className="launcher-wrapper">
<div className="launcher-menu">
<h1 className="secondMinor uppercase">
{valueX.last}.{valueX.current} {valueX.title}
</h1>
<p className="secondMinor m-t-20">{valueX.time}</p>
<div
className="major-button center-elements m-t-20"
onClick={() => setState(!state)}
>
<div>Button</div>
<div
className="cursor-pointer svg-lesson"
style={{ fill: "white" }}
>
<Play width="30px" height="30px" />
</div>
</div>
</div>
</div>
</div>
</div>
);
}
the following code is part of my test
function initWrapper(initStore, props) {
wrapper = mount(
<Provider store={initStore}>
<MemoryRouter>
<Router>
<Component {...minProps} />
</Router>
</MemoryRouter>
</Provider>
);
return;
}
it("Run index is ran", async () => {
initWrapper(storeFullyFill, minProps);
expect(wrapper.find("Memo(component)").length).toBe(1);
expect(wrapper).toBeTruthy();
});
One of my svg:
<?xml version="1.0" encoding="UTF-8"?>
<svg width="current" height="current" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>ICONS/PLAY</title>
<defs>
<polygon id="path-1-play" points="0 0 9.75 6.19051525 0 12"></polygon>
</defs>
<g id="ICONS/PLAY" stroke="none" stroke-width="1" fill="current" fill-rule="evenodd">
<g id="Rectangle-play" transform="translate(10.000000, 9.000000)">
<mask id="mask-2-play" fill="current">
<use xlink:href="#path-1-play"></use>
</mask>
<use id="play-mask-1" fill="current" fill-rule="nonzero" xlink:href="#play-1"></use>
<rect fill="current" mask="url(#mask-2-play)" x="-10" y="-9" width="30" height="30"></rect>
</g>
</g>
</svg>
Do you thing this is temporary warning that'll be fix ? or can I do something to fix / mute it ?
You need to mock SVG files as jest struggles with SVG for some reason.
Create a __mock__ folder. The directory should be adjacent to node_modules. The setup should be:
svgrMock.js:
//__mocks__/svgrMock.js
import * as React from 'react'
export default 'SvgrURL'
export const ReactComponent = 'svg'
package.json
...
"jest": {
"moduleNameMapper": {
"\\.svg": "<rootDir>/__mocks__/svgrMock.js"
}
},
Just bear in mind only the following are valid methods for importing SVGs after this fix:
import logoURL from '../assets/logo.svg'
// and
import { ReactComponent as Logo } from '../assets/logo.svg'
Full details: https://react-svgr.com/docs/jest/
According to this:
For those finding this issue coming from other projects:
This can be fixed by adding xmlns="http://www.w3.org/2000/svg" to any offending SVG element, including <g>, <use>, <text>, or <tspan> (doesn't break spec, but does pollute attribute list).
You could also run tests in something other than __DEV__ mode, or add the is attribute (breaks HTML spec, requires test selectors to be rewritten).
https://github.com/facebook/react/blob/993ca533b42756811731f6b7791ae06a35ee6b4d/packages/react-dom/src/client/ReactDOMComponent.js#L442

Dynamically load svgs as components in react

My issue is basically this:
I have a bunch of svg images all stored in a .js file like this
import React from 'react'
export const React = () =>
<svg width="40" height="40" viewBox="0 0 256 256"><ellipse cx="238.456" cy="39.252" rx="28.346" ry="26.522" fill="#c90016"/></svg>
export const Angular = () =>
<svg width="40" height="40" viewBox="0 0 256 256"><ellipse cx="238.456" cy="39.252" rx="28.346" ry="26.522" fill="#c90016"/></svg>
export const JQuery = () =>
<svg width="40" height="40" viewBox="0 0 256 256"><ellipse cx="238.456" cy="39.252" rx="28.346" ry="26.522" fill="#c90016"/></svg>
in another component I want to render correct image based on an array, so for instance I have:
import {React,Angular,JQuery} from '../svgs'
const images = [
'React','React','JQuery','Angular','JQuery'
]
no I want to render each image, actually to do something like this
<div>
images.map (image => <image>)
</div>
you have to make some changes in svgs.js as you are exporting a named constant React again so it will throw duplication error when you import it.
import React from 'react'
export const react = () =>
<svg width="40" height="40" viewBox="0 0 256 256"><ellipse cx="238.456" cy="39.252" rx="28.346" ry="26.522" fill="#c90016"/></svg>
export const angular = () =>
<svg width="40" height="40" viewBox="0 0 256 256"><ellipse cx="238.456" cy="39.252" rx="28.346" ry="26.522" fill="#c90016"/></svg>
export const jQuery = () =>
<svg width="40" height="40" viewBox="0 0 256 256"><ellipse cx="238.456" cy="39.252" rx="28.346" ry="26.522" fill="green"/></svg>
After that import them where you want
import {react, angular, jQuery} from '../svgs'
// don't use quotes as it represents string
// const images = [
// 'react', 'angular', 'jQuery'
// ]
const images = [
react, angular, jQuery
]
And last use them in jsx
render() {
return (
<div>
// use uppercase letter for React component
{images.map((Image, index) => <Image key={index} />)}
</div>
)
}

Resources