React & Progressbar.js: Custom shaped progress circle - javascript

I found this cool loading progress project available in JavaScript and React. In their example at the bottom they have a progress loading circle in shape of a .svg picture (Heart). I want to do the same with a custom .svg and in React, but I struggle to get a working example. I installed it with npm, but how to import it and how to use it?
// import
import progressBar from 'react-progressbar.js';
// or like in their outdated example?
// var ProgressBar = require('react-progressbar.js')
...
// Why progressBar.Circle, can't I import Circle directly from the package?
var Circle = progressBar.Circle;
let options = {
strokeWidth: 2,
};
let containerStyle = {
width: '200px',
height: '200px',
};
...
return (
<Circle
progress={1}
text={'test'}
options={options}
initialAnimate={true}
containerStyle={containerStyle}
containerClassName={'.progressbar'}
/>
);
At the moment I get the Error:
Error: Element ref was specified as a string (progressBar) but no
owner was set.
If someone could make a minimal working example how to use react-progressbar.js that would be nice.
I am also open to alternatives, if they are easy to use without too much code.
Thanks in advance.

The package is a bit outdated, so it is based on an older version of react that allowed refs to be simple strings. Newer versions of react (as the one your are probably using) does not allow that anymore, hence the error.
See: Element ref was specified as a string (map) but no owner was set
You should either:
Downgrade your react version (I would not suggest to use an older version just to use an outdated dependency)
Use a different library (A quick google search revelead a lot of react progressbar packages)
Use the Javascript version of this library, and wrap the functionalty inside a custom react component
Something along the lines of
import { Circle } from 'progressbar.js'
export default class CircleProgress extends React.Component<Props, State> {
componentDidMount() {
var bar = new Circle('#container', {easing: 'easeInOut'});
bar.animate(1);
}
render() {
return (
<div id="container"></div>
)
}
}

#gbalduzzi 's answer is the right and accepted one.
I still wanted to post a full working example with a custom .svg image:
import React from 'react';
import ProgressBar from 'progressbar.js';
export default class CustomProgressShape extends React.Component {
componentDidMount() {
var svgPath = document.getElementById('heart-path');
var path = new ProgressBar.Path(svgPath, {
easing: 'easeInOut',
duration: 5000,
step: function(state, circle) {
if (circle.value() === 1) {
circle.set(0);
circle.animate(1.0);
}
},
});
path.set(0);
path.animate(1.0);
}
render() {
return (
<>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" x="0px" y="0px" viewBox="0 0 100 100">
<path
fill-opacity="0"
stroke-width="0.5"
stroke="#f4f4f4"
d="M81.495,13.923c-11.368-5.261-26.234-0.311-31.489,11.032C44.74,13.612,29.879,8.657,18.511,13.923 C6.402,19.539,0.613,33.883,10.175,50.804c6.792,12.04,18.826,21.111,39.831,37.379c20.993-16.268,33.033-25.344,39.819-37.379 C99.387,33.883,93.598,19.539,81.495,13.923z"
/>
<path
id="heart-path"
fill-opacity="0"
stroke-width="0.6"
stroke="#555"
d="M81.495,13.923c-11.368-5.261-26.234-0.311-31.489,11.032C44.74,13.612,29.879,8.657,18.511,13.923 C6.402,19.539,0.613,33.883,10.175,50.804c6.792,12.04,18.826,21.111,39.831,37.379c20.993-16.268,33.033-25.344,39.819-37.379 C99.387,33.883,93.598,19.539,81.495,13.923z"
/>
</svg>
<div id="container"></div> <div id="heart-path2"></div>
</>
);
}
}

Related

How to present a blurred version of an image in NextJS as it's being downloaded?

I'm looking for a similar effect as this one below:
I have done something similar with Gatsby, but I'm wondering if this is possible to do with NextJS?
For version above 10.2 Nextjs is providing built in blur image in Image component.
After spending hours I found this article
First you need to update your nextJs app to 10.2.4 or above version.
yarn add next#10.2.4
Then implement your nextJs Image component with two addtional props blurDataURL and placeholder. Check the code example below.
<Image
className="image-container"
src="/images/high-quality-image.jpg"
width={250}
height={240}
quality={75}
blurDataURL="/images/path-to-blur-image.jpg"
placeholder="blur"
/>
Next.js Update:
Next.js now support placeholder="blur" you can use it in the image component:
<Image src={author} alt="Picture of the author" placeholder="blur" />
You can do it without using an external library with not much code.
https://codepen.io/darajava/pen/GRZzpbB?editors=0110
I added animations in there too on load. The image will fit to the width of its parent container. Usage is simple:
<LoadImage largeImageSrc={url} smallImageSrc={url} />
With Next.js v11, this is supported internally:
import Image from 'next/image';
import someImg from 'path/to/some/image.png';
// ...
<Image src={someImg} placeholder="blur" />
References:
https://nextjs.org/blog/next-11#image-placeholders
https://nextjs.org/docs/api-reference/next/image#placeholder
Also, this issue discussed a whole lot of alternatives, which can be helpful if you are using lower versions or trying to use this with non-static images.
Official demo: https://image-component.nextjs.gallery/placeholder
The example can be animated if you want: (related)
import Image from 'next/image';
import { useState } from 'react';
import ViewSource from '../components/view-source';
import mountains from '../public/mountains.jpg';
const PlaceholderBlur = () => {
const [loaded, setLoaded] = useState(false);
return (
<>
<ViewSource pathname="pages/placeholder.js" />
<h1>Image Component With Placeholder Blur</h1>
<Image
alt="Mountains"
src={mountains}
placeholder="blur"
width={700}
height={475}
className={loaded ? 'unblur' : ''}
onLoadingComplete={() => setLoaded(true)}
/>
<style jsx global>{`
.unblur {
animation: unblur 1s linear;
}
#keyframes unblur {
from {
filter: blur(20px);
}
to {
filter: blur(0);
}
}
`}</style>
</>
);
};
export default PlaceholderBlur;
Result:
How does this compare with other options??
You get to keep using the awesome next/image instead of unoptimized img tag or not-so-optimized third-party implementations.
No need to add additional dependencies, i.e. bundle size won't be effected much.
No need to manually generate a tiny image to show as preview. Next.js automatically generates a very small (<10px) image for you.
import Image from 'next/image';
import imageUrlBuilder from '#sanity/image-url';
import sanity from '../../utils/sanity';
export function urlFor(source) {
return imageUrlBuilder(sanity).image(source);
}
const SanityImage = ({ src, width, height, alt, ...props }) => (
<Image
src={urlFor(src).width(width).height(height).url()}
width={width}
height={height}
layout="responsive"
placeholder="blur"
alt={alt || 'kickoff'}
blurDataURL="/images/coverImage.png"
{...props}
/>
);
export default SanityImage;

react + styled-components using state to customize style

New to react and styled-components and have probably got myself in a muddle through not understanding how it all works.
Let's start from the top.
I have a simple page (App.js) that renders two components "Knobs".
I want to pass each 'Knob' one or more properties so it can calculate its size and other relevant instance props. In the example below, one know is 200px in size, and it's sister is a 100px.
import React from 'react';
import Knob from './components/knob.js'
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
hello world
<Knob size={200} />
<Knob size={100} />
</header>
</div>
);
}
export default App;
So far so good.
Now inside the Knob component, I do all my transformations and ultimately have a scaled Knob.
The knob is a svg based component (abbreviated below but still long, sorry).
So - the good news is that it all works! But I know I am approaching this wrong.
In order to get it to work and use the this.state.size to calculate the appropriate font size for the component , I had to move the styled-component object into the class...and create an empty declaration outside the class (Styles).
So - my ask is two-fold:
I think my approach is philosophically damaged...and would love experts here to unscramble my brain.
How would you edit the code to make it not just work, but work right!
a) It seems to me that the entire Styles declaration belongs outside the class.
b) No idea why I have to reference this.state.xxxx twice
c) I think I am also mixing up the use of props and state.
Other than that it's perfect (:. But -- as you see from the screenshot below...it actually works.
Ugh.
import React from 'react'
import { Knob, Pointer, Value, Scale, Arc } from 'rc-knob'
import styled from 'styled-components';
// this is my weird hack to get things working. Declare Styles outside of the class.
var Styles = {}
export default class MyKnob extends React.Component {
constructor(props) {
super(props)
this.state = {
size: props.size,
value: props.value,
radius: (props.value/2).toString(),
fontSize: (props.size * .2)
}
//Now define styles inside the class and i can use the fontsize that is derived from the size passed by the parent component!
Styles = styled.div`
.vpotText {
fill: green;
font-size: ${this.state.fontSize+'px'};
}
`
}
// no idea why I need this block....but without it I get a whole bunch of
// error TS2339: Property 'value' does not exist on type 'Readonly<{}>'.
state = {
value: 50,
size: 100,
radius: '50',
fontSize: 12
}
static defaultProps = { value: 50, size: 100};
render(){
const customScaleTick = ({}) //abbreviated for readability.
return (
<Styles>
<Knob size={this.state.size}
angleOffset={220}
angleRange={280}
steps={10}
min={0}
max={100}
// note use of this.state.value to set parameters that affect the sizing/display of the component
value={this.state.value}
onChange={value => console.log(value)}
>
<Scale steps={10} tickWidth={1} tickHeight={2} radius={(this.state.size/2)*0.84} color='grey' />
<Arc arcWidth={2} color="#4eccff" background="#141a1e" radius = {(this.state.size/2)*0.76} />
<defs>
{/* GRADIENT DEFINITIONS REMOVED FOR READABILITY */}
</defs>
{/* NOTE: EXTENSIVE USE OF this.state.size TO ENSURE ALL PARTS OF THE COMPONENT ARE SCALED NICELY */}
<circle cx={this.state.size/2} cy={this.state.size/2} rx={(this.state.size/2)*0.8} fill = "url(#grad-dial-soft-shadow)" />
<ellipse cx={this.state.size/2} cy={(this.state.size/2)+2} rx={(this.state.size/2)*0.7} ry={(this.state.size/2)*0.7} fill='#141a1e' opacity='0.15' ></ellipse>
<circle cx={this.state.size/2} cy={this.state.size/2} r={(this.state.size/2)*0.7} fill = "url(#grad-dial-base)" stroke='#242a2e' strokeWidth='1.5'/>
<circle cx={this.state.size/2} cy={this.state.size/2} r={(this.state.size/2)*0.64} fill = 'transparent' stroke='url(#grad-dial-highlight)' strokeWidth='1.5'/>
<Pointer width={(this.state.size/2)*0.05} radius={(this.state.size/2)*0.47} type="circle" color='#4eccff' />
{/* THIS IS THE TRICKY ONE! */}
{/* IN ORDER TO GET THE FONT SIZE RIGHT ON THIS ELEMENT (svg) I NEED THE STYLE */}
<Value
marginBottom={(this.state.size-(this.state.fontSize)/2)/2}
className="vpotText"
/>
</Knob>
</Styles>
)}
}
here's a pic of the output:
a) This is how we use props variables in styled components:
const Styles = styled.div`
.vpotText {
fill: green;
font-size: ${props => props.fontSize}px;
};
`;
b) That way you won't need to call the state twice
render(){
return(
<Styles fontSize={this.state.fontSize}>
...
</Styles>
)}
styled-components are really cool once you get the hang of them.
d) Also, I suggest you make value it's own component instead of wrapping it and calling the class.
const StyledValue = styled(Value)`
fill: green;
font-size: ${props => props.fontSize}px;
`;
This looks like it would be a good use case for passing a prop into a Styled Component. It would look something like this:
var Styles = styled.div`
.vpotText {
fill: green;
font-size: ${props => props.size};
}
`
<Styles size={someSize}>
...
</Styles>
You can find the documentation here:
https://styled-components.com/docs/basics#passed-props

Import SVG icons from another folder

I want to import svg icons from my icon folder, to change the color of the icons I have to give fill parameter to path inside SVG. So i need import and to show only SVG but not like <img src="icon.svg"/>.
I first put one SVG right inside the component and it shows no problem.
but I have a lot of icons I can’t put everything inside the component.
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
export const Gunicon = ({ name, height, width, color, bold }) => {
const classes = makeStyles(theme => {
/* i want to give style like this */
return {
gunicon: {
"& svg": {
height,
width,
"& path": {
fill: color,
stroke: color,
strokeWidth: bold
}
}
}
};
});
/* this is working. but i dont like this */
const ico = (
<svg height={height} width={width} viewBox="0 0 512 512">
<path d="test svg" fill={color} stroke={color} strokeWidth={bold} />
</svg>
);
return (
<div className={classes.gunicon}>
{/* dont working */}
{require(`./icons/${name}.svg`)}
{/* working */}
{ico}
</div>
);
};
when i am writing as {require("./icons/${name}.svg")}. this is showing me only path to SVG. But i want import every SVG by name and only SVG
Ok, my friend.
I guess you are using create-react-app and you don't need to configure your webpack (I hope nowadays no one knows what is webpack)
So, what did they in create-react-app is added loader for svg for you, and map source of the loaded asset to the field ReactComponent. What you need to do is:
const { ReactComponent: Icon } = require(`./icons/${name}.svg`);
return (
<Icon />
);

Using require for SVG files in React Native

Im using react-native-svg and I'm fully aware of their way to use local svg files as shown here.
What I would like to know is if there is a way to use require for svg file paths. e.g.
<Svg width={50} height={50} fill={"#CCC"} source={require("./path/to/file.svg")} />
That way I would be able to store the require in a variable and use it like:
const myImage = require("./path/to/file.svg")
<Svg width={50} height={50} fill={"#CCC"} source={myImage} />
Any ideias?
EDIT FOR MORE DETAIL
Im developing a white label app so I have a config.js file with some color values, API endpoints and source images. e.g.
//config.js
const coBrandConfig = {
firstapp: {
Target: {
BRAND_TARGET: "firstApp"
},
Login: {
LOGIN_BACKGROUND_IMAGE: require("./path/to/file.png"),
LOGIN_LOGO_IMAGE: require("./path/to/file.png"),
LOGIN_TITLE_TEXT: "FIRST APP",
LOGIN_STATUSBAR_CONTENT: "light-content",
LOGIN_BACKGROUND_COLOR: "#333" ,
LOGIN_SIMPLE_TEXT_COLOR: "#FFF",
LOGIN_TEXTINPUT_BACKGROUD_COLOR: "#FFF",
LOGIN_LOGIN_BUTTON_COLOR: "#009933",
},
},
}
module.exports = coBrandConfig["firstapp"]
Then I have a styles.js that gets and applies all of these values, which can change depending on the App variant. e.g.
import React from 'react'
import { StatusBar, Image } from "react-native"
import styled from 'styled-components/native'
import CoBrandConfig from "./config/config.js"
export function MainStyle () {
return(
<>
<StatusBar barStyle={`${CoBrandConfig.Login.LOGIN_STATUSBAR_CONTENT}`} backgroundColor={CoBrandConfig.Login.LOGIN_BACKGROUND_COLOR} />
<Image source={CoBrandConfig.Login.LOGIN_LOGO_IMAGE} />
<Svg width={50} height={50} fill={"#CCC"} source={CoBrandConfig.Login.MY_SVG} /> //HERES WHAT I WANT TO DO
<TitleText>{CoBrandConfig.Login.LOGIN_TITLE_TEXT}</TitleText>
</>
)
}
Thats why I would like to pass a require("./path/to/file.svg") to a variable.
I'm a bit late, but try adding .default.src after your require:
source={require("./path/to/file.svg").default.src}
I use this library, I hope it will be useful
npm install 'react-native-svg';
then
import { SvgUri } from 'react-native-svg';
<SvgUri
width="100%"
height="100%"
uri="url svg"
/>
Try this one
import SvgUri from 'react-native-svg-uri';
import svgExample from './file.svg';
return:
<SvgUri
width="250"
height="250"
svgXmlData={svgExample}
/>

How to show SVG file on React Native?

I want to show svg files (I have bunch of svg images) but the thing I couldn't find the way to show. I tried to use Image and Use components of react-native-svg but they don't work with that. And I tried to do that with native way but it's really hard work to show just svg image.
Example code:
import Svg, {
Use,
Image,
} from 'react-native-svg';
<View>
<Svg width="80" height="80">
<Image href={require('./svg/1f604.svg')} />
</SvgRn>
</View>
Also I know the react native doesn't support svg basically but I think someone fixed this problem with tricky way (with/without react-native-svg)
I used the following solution:
Convert .svg image to JSX with https://svg2jsx.herokuapp.com/
Convert the JSX to react-native-svg component with https://react-svgr.com/playground/?native=true (make sure the "React Native" checkbox is checked)
I posted another solution (react-native-vector-icons) here. This approach use a vector font (from SVG) instead a SVG file.
PS: react-native-vector-icons is the best approach to deal with SVG in react-native, it's also works in iOS and android. You will be able to change the color and size of your vector icon.
If you want to insert a svg directly into your app, you can try a 3rd party library : react-native-svg. With more than 3k stars in github it's one of the best approach.
Install any one of these using npm:
react-native-svg
react-native-svg-uri
Then link it to native using
react-native link react-native-svg
An example with react-native-svg-uri:
import * as React from 'react';
import SvgUri from 'react-native-svg-uri'; // SVG Package
import testSvg from './test.svg'; // SVG File
export default () => (
<SvgUri
width="200"
height="200"
svgXmlData={testSvg}
/>
);
Install react-native-svg-transformer
npm i react-native-svg-transformer --save-dev
I'm using SVG as following and it works fine
import LOGOSVG from "assets/svg/logo.svg"
in render
<View>
<LOGOSVG
width="100%"
height="70%"
/>
</View>
I've tried all the above solutions and other solutions outside of the stack and none of working for me. finally, after long research, I've found one solution for my expo project.
If you need it to work in expo, one workaround might be to use https://react-svgr.com/playground/ and move the spreading of props to a G element instead of the SVG root like this:
import * as React from 'react';
import Svg, { G, Path } from 'react-native-svg';
function SvgComponent(props) {
return (
<Svg viewBox="0 0 511 511">
<G {...props}>
<Path d="M131.5 96c-11.537 0-21.955 8.129-29.336 22.891C95.61 132 92 149.263 92 167.5s3.61 35.5 10.164 48.609C109.545 230.871 119.964 239 131.5 239s21.955-8.129 29.336-22.891C167.39 203 171 185.737 171 167.5s-3.61-35.5-10.164-48.609C153.455 104.129 143.037 96 131.5 96zm15.92 113.401C142.78 218.679 136.978 224 131.5 224s-11.28-5.321-15.919-14.599C110.048 198.334 107 183.453 107 167.5s3.047-30.834 8.581-41.901C120.22 116.321 126.022 111 131.5 111s11.28 5.321 15.919 14.599C152.953 136.666 156 151.547 156 167.5s-3.047 30.834-8.58 41.901z" />
<Path d="M474.852 158.011c-1.263-40.427-10.58-78.216-26.555-107.262C430.298 18.023 405.865 0 379.5 0h-248c-26.365 0-50.798 18.023-68.797 50.749C45.484 82.057 36 123.52 36 167.5s9.483 85.443 26.703 116.751C80.702 316.977 105.135 335 131.5 335a57.57 57.57 0 005.867-.312 7.51 7.51 0 002.133.312h48a7.5 7.5 0 000-15h-16c10.686-8.524 20.436-20.547 28.797-35.749 4.423-8.041 8.331-16.756 11.703-26.007V503.5a7.501 7.501 0 0011.569 6.3l20.704-13.373 20.716 13.374a7.498 7.498 0 008.134 0l20.729-13.376 20.729 13.376a7.49 7.49 0 004.066 1.198c1.416 0 2.832-.4 4.07-1.2l20.699-13.372 20.726 13.374a7.5 7.5 0 008.133 0l20.732-13.377 20.738 13.377a7.5 7.5 0 008.126.003l20.783-13.385 20.783 13.385a7.5 7.5 0 0011.561-6.305v-344a7.377 7.377 0 00-.146-1.488zM187.154 277.023C171.911 304.737 152.146 320 131.5 320s-40.411-15.263-55.654-42.977C59.824 247.891 51 208.995 51 167.5s8.824-80.391 24.846-109.523C91.09 30.263 110.854 15 131.5 15s40.411 15.263 55.654 42.977C203.176 87.109 212 126.005 212 167.5s-8.824 80.391-24.846 109.523zm259.563 204.171a7.5 7.5 0 00-8.122 0l-20.78 13.383-20.742-13.38a7.5 7.5 0 00-8.131 0l-20.732 13.376-20.729-13.376a7.497 7.497 0 00-8.136.002l-20.699 13.373-20.727-13.375a7.498 7.498 0 00-8.133 0l-20.728 13.375-20.718-13.375a7.499 7.499 0 00-8.137.001L227 489.728V271h8.5a7.5 7.5 0 000-15H227v-96.5c0-.521-.054-1.03-.155-1.521-1.267-40.416-10.577-78.192-26.548-107.231C191.936 35.547 182.186 23.524 171.5 15h208c20.646 0 40.411 15.263 55.654 42.977C451.176 87.109 460 126.005 460 167.5V256h-.5a7.5 7.5 0 000 15h.5v218.749l-13.283-8.555z" />
<Path d="M283.5 256h-16a7.5 7.5 0 000 15h16a7.5 7.5 0 000-15zM331.5 256h-16a7.5 7.5 0 000 15h16a7.5 7.5 0 000-15zM379.5 256h-16a7.5 7.5 0 000 15h16a7.5 7.5 0 000-15zM427.5 256h-16a7.5 7.5 0 000 15h16a7.5 7.5 0 000-15z" />
</G>
</Svg>
);
}
export default function App() {
return (
<SvgComponent width="100%" height="100%" strokeWidth={5} stroke="black" />
);
}
After trying many ways and libraries I decided to create a new font (with Glyphs or this tutorial) and add my SVG files to it, then use "Text" component with my custom font.
Hope this helps anyone that has the same problem with SVG in react-native.
Method that is working for me in 2022 for anyone that is still stuck on this, react native version 0.63.4, react-native-svg version ^12.1.0, you may be able to use this method for other versions too but always check the docs for the library first:
Install react-native-svg-transformer by running this command yarn add react-native-svg-transformer --dev or using npm if you prefer npm i react-native-svg react-native-svg-transformer --save-dev
Install babel-plugin-inline-import by running yarn add babel-plugin-inline-import --dev
You need to update your metro.config.js file to looks like this:
const { getDefaultConfig } = require('metro-config');
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts }
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false
}
})
},
resolver: {
assetExts: assetExts.filter(ext => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg']
}
};
})();
Make sure to import your svg file into your component like this: import CreditCard from '../assets/CreditCard'; please note how I am not adding the .svg file extension to the import, this is because it was giving me this error view config getter callback react-native-svg while running it but removing it seems to make it run smoothly. You can add your file this way as a jsx element like that <CreditCard />.
Finally Restart your packager by stopping it using ^Ctrl + C on mac then restart it again with this command yarn start:dev --reset-cache and install the app again.
you can do this using a simple way
first, you should take installation,
npm install -s react-native-svg
react-native link react-native-svg
npm install -s react-native-svg-transformer
then, you should add the following code in your metro.config.js file
const { getDefaultConfig } = require("metro-config");
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts }
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve("react-native-svg-transformer")
},
resolver: {
assetExts: assetExts.filter(ext => ext !== "svg"),
sourceExts: [...sourceExts, "svg"]
}
};
})();
after that, you should create a new file in your root directory with the name of declarations.d.js with the following code
declare module "*.svg" {
import { SvgProps } from "react-native-svg";
const content: React.FC<SvgProps>;
export default content;
}
Finally,
This is a import mathod
import USER from "../../assets/icons/user.svg"
and, this is for jsx
<USER width="100%" height="100%"/>
SVG does not support directly in Native applications. For displaying those we need to take help of third party modules
Please follow these steps
step 1. Install react-native-svg and react-native-svg-transformer
npm i react-native-svg react-native-svg-transformer
step 2. Add file metro.config.js if it doesn't exist
const { getDefaultConfig } = require("metro-config");
module.exports = (async () => {
const {
resolver: {
sourceExts,
assetExts
}
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve("react-native-svg-transformer")
},
resolver: {
assetExts: assetExts.filter(ext => ext !== "svg"),
sourceExts: [...sourceExts, "svg"]
}};
})();
step 3. Use SVG file like a React component
import Invest from '../assets/invest.svg'; // import SVG
return (
<View>
<Invest /> // Use SVG as component
</View>
)
I had the same problem. I'm using this solution I found: React Native display SVG from a file
It's not perfect, and i'm revisiting today, because it performs a lot worse on Android.
Use https://github.com/kristerkari/react-native-svg-transformer
In this package it is mentioned that .svg files are not supported in React Native v0.57 and lower so use .svgx extension for svg files.
For web or react-native-web use https://www.npmjs.com/package/#svgr/webpack
To render svg files using react-native-svg-uri with react-native version 0.57 and lower, you need to add following files to your root project
Note: change extension svg to svgx
step 1: add file transformer.js to project's root
// file: transformer.js
const cleanupSvg = require('./cleanup-svg');
const upstreamTransformer = require("metro/src/transformer");
// const typescriptTransformer = require("react-native-typescript-transformer");
// const typescriptExtensions = ["ts", "tsx"];
const svgExtensions = ["svgx"]
// function cleanUpSvg(text) {
// text = text.replace(/width="([#0-9]+)px"/gi, "");
// text = text.replace(/height="([#0-9]+)px"/gi, "");
// return text;
// }
function fixRenderingBugs(content) {
// content = cleanUpSvg(content); // cleanupSvg removes width and height attributes from svg
return "module.exports = `" + content + "`";
}
module.exports.transform = function ({ src, filename, options }) {
// if (typescriptExtensions.some(ext => filename.endsWith("." + ext))) {
// return typescriptTransformer.transform({ src, filename, options })
// }
if (svgExtensions.some(ext => filename.endsWith("." + ext))) {
return upstreamTransformer.transform({
src: fixRenderingBugs(src),
filename,
options
})
}
return upstreamTransformer.transform({ src, filename, options });
}
step 2: add rn-cli.config.js to project's root
module.exports = {
getTransformModulePath() {
return require.resolve("./transformer");
},
getSourceExts() {
return [/* "ts", "tsx", */ "svgx"];
}
};
The above mentioned solutions will work in production apps too ✅
you can convert any SVG to a component and make it reusable.
here is my answer for the easiest way you can do it
SVG to component
import React from 'react'
import SvgUri from 'react-native-svg-uri';
export default function Splash() {
return (
<View style={styles.container}>
{/* provided the svg file is stored locally */}
<SvgUri
width="400"
height="200"
source={require('./logo.svg')}
/>
{/* if the svg is online */}
<SvgUri
width="200"
height="200"
source={{ uri: 'http://thenewcode.com/assets/images/thumbnails/homer-simpson.svg' }}
/>
<Text style={styles.logoText}>
Text
</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
logoText: {
fontSize: 50
}
});
Using svg in a react native project is not as simple as using it in the web, although it is not that difficult either.
Many of the above answers are complicated in my view. So, here is the solution that I use and like.
Since react native does not support the svg file format directly you need to follow some steps to get it working.
1. Install react-native-svg
npm install react-native-svg --save
This package will help you render the svg file
2. Install react-native-svg-transformer
npm install react-native-svg-transformer --save
This package will transform your svg to the format that react native understands.
3. Import the svg as normal component
import Belsvg from "../assets/belsvg.svg";
While importing give it a name that you like now this svg is transformed into a react native component by the react-native-svg-transformer
4. Now you can use this as a normal component
For a detailed explanation and example visit the link :
https://kumar2396jayant.medium.com/how-to-use-svg-in-react-native-e581eca59534
ps: I would like to thank Jayant kumar yadav for posting that blog
If you're using React Native with TypeScript, here is the step-by-step solution:
1. Install react-native-svg
npm install react-native-svg
or
yarn add react-native-svg
2. Link native code
cd ios && pod install
3. Install react-native-svg-transformer
npm install --save-dev react-native-svg-transformer
or
yarn add --dev react-native-svg-transformer
4. Configure the react native packager
For React Native v0.59 or newer:
Merge the contents from your project's metro.config.js file with this config (create the file if it does not exist already).
const { getDefaultConfig } = require("metro-config");
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts }
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve("react-native-svg-transformer")
},
resolver: {
assetExts: assetExts.filter(ext => ext !== "svg"),
sourceExts: [...sourceExts, "svg"]
}
};
})();
This is how my merged metro.config.js looks like:
const { getDefaultConfig } = require('metro-config')
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts },
} = await getDefaultConfig()
return {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
},
}
})()
5. TypeScript configuration:
If you are using TypeScript, you need to add this to your declarations.d.ts file (create one if you don't have one already):
declare module "*.svg" {
import React from 'react';
import { SvgProps } from "react-native-svg";
const content: React.FC<SvgProps>;
export default content;
}
6. Usage
...
import LogSvg from '../assets/images/Logo.svg'
export function LandingScreen(): JSX.Element {
...
return (
<View style={style.mainContainer}>
<LogSvg style={style.logoContainer} />
</View>
)
}
...
Note: Svg does not work for android release versions so do not consider for android. It will work for android in debug mode only. But it works fine for ios.
Use https://github.com/vault-development/react-native-svg-uri
Install
npm install react-native-svg-uri --save
react-native link react-native-svg # not react-native-svg-uri
Usage
import SvgUri from 'react-native-svg-uri';
<SvgUri source={require('./path_to_image/image.svg')} />
I use these two plugins,
[react-native-svg] https://github.com/react-native-community/react-native-svg)
[
react-native-svg-transformer] https://github.com/kristerkari/react-native-svg-transformer)
First off all,
You need to install that plugin. After that you need to change your metro.config.js, with this code.
const { getDefaultConfig } = require("metro-config");
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts }
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve("react-native-svg-transformer")
},
resolver: {
assetExts: assetExts.filter(ext => ext !== "svg"),
sourceExts: [...sourceExts, "svg"]
}
};
})();
For more details, you can visit this link
https://medium.com/game-development-stuff/how-to-import-svgs-on-react-native-dc76b56403ba
https://medium.com/#majirosstefan/the-svgs-inside-react-native-0-61-5-and-storybook-566bcd43ba94
First install react-native-svg to your project.
Open https://react-svgr.com/ and go to the playground and select React native
Copy paste your SVG code and used the output code.
Ex:- This is the sample SVG that I have got.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="17" height="17" viewBox="0 0 17 17">
<defs>
<path id="a" d="M16.191 1.027L9.023 8.196l7.168 7.168-.83.83-7.169-7.167-7.168 7.168-.831-.831L7.36 8.196.19 1.027l.832-.83L8.19 7.366 15.359.197z"/>
</defs>
<g fill="none" fill-rule="evenodd">
<mask id="b" fill="#fff">
<use xlink:href="#a"/>
</mask>
<use fill="#FFF" fill-rule="nonzero" xlink:href="#a"/>
<g fill="#3D3D3D" mask="url(#b)">
<path d="M0 0h16v16H0z"/>
</g>
</g>
</svg>
Output:-
import * as React from "react"
import Svg, { Defs, Path, G, Mask, Use } from "react-native-svg"
function SvgComponent(props) {
return (
<Svg
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
width={17}
height={17}
viewBox="0 0 17 17"
{...props}
>
<Defs>
<Path
id="prefix__a"
d="M16.191 1.027L9.023 8.196l7.168 7.168-.83.83-7.169-7.167-7.168 7.168-.831-.831L7.36 8.196.19 1.027l.832-.83L8.19 7.366 15.359.197z"
/>
</Defs>
<G fill="none" fillRule="evenodd">
<Mask id="prefix__b" fill="#fff">
<Use xlinkHref="#prefix__a" />
</Mask>
<Use fill="#FFF" fillRule="nonzero" xlinkHref="#prefix__a" />
<G fill="#3D3D3D" mask="url(#prefix__b)">
<Path d="M0 0h16v16H0z" />
</G>
</G>
</Svg>
)
}
export default SvgComponent
** But when I use the above code in my project and it does not render properly. So I done it as below and somehow it works for me.
import * as React from "react"
import Svg, { Path, G } from "react-native-svg"
function SvgComponent(props) {
return (
<Svg
width={17}
height={17}
{...props}
>
<G>
<Path
id="prefix__a"
d="M16.191 1.027L9.023 8.196l7.168 7.168-.83.83-7.169-7.167-7.168 7.168-.831-.831L7.36 8.196.19 1.027l.832-.83L8.19 7.366 15.359.197z"
/>
</G>
</Svg>
)
}
export default SvgComponent
I've written a new library to be able to use native vector assets based on .svg files called react-native-vector-image. It doesn't need any custom babel transforms and you can use it like you would for other bitmap based assets, but there is an extra step in transforming the svg to the native equivalents (you only need to run it when you add new assets though).
Using your example the code would look something like this
import VectorImage from 'react-native-vector-image';
<View>
<VectorImage source={require('./svg/1f604.svg')} />
</View>
The best solution that worked for me on both platforms is https://github.com/seekshiva/react-native-remote-svg.
I additionally did a conditional render with https://github.com/react-native-svg/react-native-svg to have the best result on both platforms, because there's an know issue with Android as stated here https://stackoverflow.com/a/54182426/4934132.
Just in case I have ErrorBoundry to cover the exceptions.
It looks like this:
<ErrorBoundary
FallbackComponent={error => ErrorFallback(error)}
onReset={() => {
// reset the state of your app so the error doesn't happen again
}}
onError={myErrorHandler}
>
{Platform.OS === 'ios' ?
<SvgCssUri width="100%" height="100%" uri={url} />
:
<Image
source={{ uri: url }}
style={{ width: "100%", height: '100%' }}
/>
}
</ErrorBoundary>
You can try Converting .svg image to JSX with https://svg2jsx.herokuapp.com/
Then save the JSX code in assest/SVGFile.js
Then import it, for example import open from "../../../../assets/SVGFile";
Lastly add <SvgXml xml={open} scaleX={2} scaleY={2} width={20} height={20} />
Make sure to install, yarn add react-native-svg
All the best!
Incase anyone is stuck on this bug in 2022, here's the solution that worked for me:
Install react-native-svg-transformer:
yarn add react-native-svg-transformer --dev
or using npm
npm install react-native-svg-transformer --save-dev
Install react-native-svg:
yarn add react-native-svg
or using npm
npm install react-native-svg --save
Update your metro.config.js file to looks like this:
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config');
module.exports = (() => {
const {
resolver: {
sourceExts,
assetExts
}
} = getDefaultConfig(__dirname);
return {
transformer: {
babelTransformerPath: require.resolve("react-native-svg-transformer")
},
resolver: {
assetExts: assetExts.filter(ext => ext !== "svg"),
sourceExts: [...sourceExts, "svg"]
}};
})();
Import and display the svg image in your code like below:
import {View} from 'react-native';
import SvgImage from './path/to/svg/file.svg';
function MyComponent(){
return (
<View>
<SvgImage />
</View>
)
}
I try to use SvgCssUri and it works
import { SvgCssUri } from 'react-native-svg';
...
...
...
<SvgCssUri
style={{height: 100, width: 100}}
uri={"http://thenewcode.com/assets/images/thumbnails/homer-simpson.svg"} />
Use react-native-svg see its supports both android and Ios.
this is the basic example
import * as React from 'react';
import { SvgUri } from 'react-native-svg';
export default () => (
<SvgUri
width="100%"
height="100%"
uri="http://thenewcode.com/assets/images/thumbnails/homer-simpson.svg"
/>
);
if you want to use both normal image and svg images in one component, You can make a custom function that checks if the url has svg and return Image component or SvgUri
const ImageComponent = (Props) => {
const {Istyle, url, mode} = props;
const isSvg = url?.includes('.svg') ?? false;
if (isSvg) {
return <SvgCssUri style={Istyle} uri={defaultAvatar} />;
}
if (!isSvg) {
return <Image style={Istyle} source={{uri: url}} resizeMode={mode} />;
}
};
//let testURl = "https://avatars.dicebear.com/api/male/john.svg"
All these solutions are absolutely horrid. Just convert your SVG to a PNG and use the vanilla <Image/> component. The fact that react-native still does not support SVG images in the Image component is a joke. You should never install an additional library for such a simple task. Use https://svgtopng.com/

Categories