I am trying to render a React Component that returns an <svg> as a backgroundImage of a <div>.
Currently I am using ReactDOMServer with renderToStaticMarkup or renderToString but nothing shows up:
const SvgComponent = () => {
return (
<svg xmlns='http://www.w3.org/2000/svg'><rect fill='red' x='0' y='0' /></svg>
)
}
const ParentComponent = () => {
return (
<div
className={classes.banner}
style={{
backgroundImage: `url("data:image/svg+xml;utf8, ${ReactDOMServer.renderToStaticMarkup(<SvgComponent />)} ")`
}}
>
</div>
)
}
Would the package jsx-to-string the way to do it?
You have to use encodeURIComponent() to URI encode the SVG data because React will not render the SVG data if it is not URI encoded. So,
const svgString = encodeURIComponent(renderToStaticMarkup(<SvgComponent />));
Also, set the width and height in <rect> of SVG like,
<rect fill="red" width={100} height={100} />
So final code should look like,
import ReactDOM from "react-dom";
import { renderToStaticMarkup } from "react-dom/server";
import React from "react";
const SvgComponent = () => {
return (
<svg xmlns="http://www.w3.org/2000/svg">
<rect fill="red" width={100} height={100} />
</svg>
);
};
const ParentComponent = () => {
const svgString = encodeURIComponent(renderToStaticMarkup(<SvgComponent />));
return (
<div
style={{
backgroundImage: `url('data:image/svg+xml;utf8, ${svgString}')`,
width:500,
height:500
}}
>
</div>
)
}
ReactDOM.render(<ParentComponent />, document.getElementById("root"));
I have setup the same in CodeSandbox,
ReactDOMServer.renderToStaticMarkup uses double quotes in the markup, so it returns
<svg xmlns="http://www.w3.org/2000/svg"><rect fill="red" width="10" height="10"></rect></svg>
And because you also used double quotes in url("...") it gives invalid markup.
So just use single quotes in url('...') and it should work.
Note: This will probably break if react changes how renderToStaticMarkup is implemented!
Related
I've looked at this answer but I still can't wrap my head around about how to implement this with react-raphael. This is what my component looks like. How do I use the hover prop for Rect in react?
import React from 'react';
import {Paper, Rect} from 'react-raphael';
import "./style.less"
const SVGBoxes = () => {
return (
<Paper width={500} height={700} container={{className: "svg-parent"}}>
<Rect
x={30} y={148} width={150} height={150}
attr={{"fill":"#10a54a","stroke":"#000","stroke-width":2,"fill-opacity": 0.2}}
/>
</Paper>
)
}
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;
I want to create a custom material ui icon from a svg file with two paths.
My code using
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import { green } from '#material-ui/core/colors';
import SvgIcon from '#material-ui/core/SvgIcon';
const useStyles = makeStyles((theme) => ({
root: {
'& > svg': {
margin: theme.spacing(2),
},
},
}));
function GradIcon(props) {
return (
<SvgIcon {...props}>
<path
d="M142.45,174.613c-4.645,0-11.495-0.514-17.779-2.926l-50.271-19.366H49.774v30.162c0,9.274,6.9,19.802,15.405,23.499
l60.872,26.496c8.505,3.691,22.312,3.707,30.826,0.036l61.536-26.574c8.508-3.671,15.41-14.183,15.41-23.457v-30.162h-27.175
l-44.547,18.78C156.742,173.365,149.756,174.613,142.45,174.613z"
/>
<path
d="M6.475,112.944l121.222,46.709c8.661,3.329,22.603,3.112,31.152-0.492l115.768-48.801v71.999l-7.151,23.866h20.682
l-7.399-24.114V107.45h-0.208c4.997-3.449,3.832-7.747-3.567-10.393L159.196,55.146c-8.74-3.117-22.859-2.985-31.545,0.277
L6.529,100.99C-2.157,104.258-2.178,109.612,6.475,112.944z"
/>
</SvgIcon>
);
}
export default function SvgIconsColor() {
const classes = useStyles();
return (
<div className={classes.root}>
<GradIcon />
</div>
);
}
like in the docs didn't work out. As I am fairly new to javascript and react I thought I'd ask on here.
Thanks
I solved it by creating a separate component with the following Layout like in this tutorial:
First I converted the image online to an svg.
Then I opened it within an editor and selected the whole svg part.
This part is posted within the component like below. Be sure to disable any unnecessary properties, and set the width and height to 24. The properties below should be enough. Don't delete parts. Just comment out and try till it fits your desired output.
import React from "react";
const IconName = () => {
return (
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
version="1.1"
viewBox="<viewBox>"
<path d="<path>"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
fill="<color>"
fill-rule="evenodd"
/>
</svg>
);
};
export default IconName;
Then just import the icon to the component you need it in.
The number of paths doesn't matter, just remember to include all the properties after the path.
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
I'm React beginner. I tried and searched hours but I still don't get it.
I got (props) from App.js as a boolean. Pressed button or not.
I want:
If false, I want to change svg height to 300px, else to 20px. it should be animated. How to bind animation with spring?
Should I make const like navAnimation?
this is App.js
import React, { Component, useState } from 'react';
import {useSpring, animated} from 'react-spring';
import Subject from "./components/Subject";
import Nav from "./components/Nav";
import Sidebar from "./components/Sidebar";
import './components/event.js';
const App = () => {
const [isNavOpen, setNavOpen] = useState(false);
const navAnimation = useSpring({
height : isNavOpen ? 0 : 50,
opacity : isNavOpen ? 0 : 1
})
return(
<div className="App">
<div className="sidebar">
<Sidebar isOpen={isNavOpen} />
<button onClick={() => setNavOpen(!isNavOpen)}>open</button>
</div>
<div className="main">
<Subject />
<Nav style={navAnimation} />
</div>
</div>
);
}
export default App;
and this is Sidebar.js
const Sidebar = (props) => {
return(
<div id="sidebar">
lorem ipsum
<svg width="300" height="300">
<rect width="10vh" height="20" x="10" y="10" />
</svg>
</div>
);
}
export default Sidebar;
If you want to change between two values based on a boolean, a ternary can be used.
boolean ? ValueIfTrue : ValueIfFalse
You can embed expressions in jsx with curly braces. Based on the name of the prop key, the height will be set to 300 or 20. I would recommend setting some kind of unit rather than just 300, i.e. 300px.
Good luck with the rest of what you are building
const Sidebar = (props) => {
return(
<div id="sidebar">
lorem ipsum
<svg width="300" height={this.props.keyFromApp ? "300": "20"}>
<rect width="10vh" height="20" x="10" y="10" />
</svg>
</div>
);
}
Finally you need to make sure the child rerenders when the parents values change. Currently the parents variables aren't states. This means that if a parents variables change no rerendering of the child components
const App = () => {
const [isNavOpen, setNavOpen] = useState(true);
const navAnimation = useSpring({
height : isNavOpen ? 0 : 50,
opacity : isNavOpen ? 0 : 1