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 />
);
Related
My Problem
I have a project which requires icons everywhere. Instead of rendering a Fontawesome Icon in every script, I have a functional component which renders an icon when given props.
When calling the function, sometimes it doesn't accept the color prop. Only certain colors seem to be working, such as darkBlue, lightBlue, and green. Colors which haven't accepted the prop are defaulting to white.
I'm using Tailwindcss to inject classes into the components.
Tailwind Config
module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: {
colors: {
dark: "#121212",
white: "#fff",
secondary: "#F0A500",
lightBlue: "#0EA5E9",
darkBlue: "#2563EB",
beige: "#FDBA74",
silver: "#9CA3AF",
red: "#DC2626",
green: "#10B981",
orange: "#F97316",
hotPink: "#EC4899",
purple: "#6D28D9",
yellow: "#FDE047",
},
extend: {
},
},
plugins: [],
};
FC: Icon Render
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
// color props must be passed as a string
function Icon({ name, color, scale }) {
return (
<FontAwesomeIcon
icon={name}
className={`text-${color}`}
size={scale}
/>
);
}
export default Icon;
Calling Icon Render
import React from "react";
import Button from "../../shared/components/Button";
import Typography from "../../shared/utilities/Typography";
import Mugshot from "../../shared/assets/mugshot.jpg";
import Icon from "../../shared/components/Icon";
import {
faGlobe,
faHandSpock,
faComment,
} from "#fortawesome/free-solid-svg-icons";
import Avatar from "../../shared/components/Avatar";
function example() {
return (
<section className="section" id="home-hero">
<Typography variant="label">Some text</Typography>
<Typography variant="h2">
Some text <Icon name={faHandSpock} color="beige" />
</Typography>
</section>
);
}
export default example;
What I've Tried / Fun Facts
No errors in the console.
Some colors may be preserved tailwind color names?
Tried changing color names in tailwind config
Tried changing hex values in tailwind config
Conclusion
Edit: Discovered an easier way:
<Icon name={faHandSpock} color="text-beige" /> // full classname
// remove partial className, pass in object
function Icon({ name, color, scale }) {
return (
<FontAwesomeIcon
icon={name}
className={color}
size={scale}
/>
);
}
export default Icon;
TailwindCSS doesn't allow you to generate classes dynamically. So when you use the following to generate the class…
className={`text-${color}`}
…TailwindCSS will not pick that up as a valid TailwindCSS class and therefore will not produce the necessary CSS.
Instead, you must include the full name of the class in your source code.
For this you can create any function which returns the required string like this:
function changeFAColor (color) {
if(color === dark) return "text-dark"
(color === white) return "text-white"
(color === secondary) return "text-secondary")
.
.
.
(color === purple) return "text-purple")
(color === yellow) return "text-yellow")
}
And use it in the component
<FontAwesomeIcon
icon={name}
className={`${changeFAcolor(color)}`}
size={scale}
/>
Tailwind generates a CSS file which contains only the classes that you've used in the project.
The problem you're experiencing is because Tailwind doesn't recognise the generated class you're applying in "FC: Icon Render". In particular, this line:
className={`text-${color}`}
To quote the documentation:
The most important implication of how Tailwind extracts class names is
that it will only find classes that exist as complete unbroken strings
in your source files.
If you use string interpolation or concatenate partial class names
together, Tailwind will not find them and therefore will not generate
the corresponding CSS:
https://tailwindcss.com/docs/content-configuration#class-detection-in-depth
To resolve your problem, either pass in the full class name instead of generating it or safelist all of your text-{color} classes in your config file.
Assign your colors to a variable:
const colors = {
dark: "#121212",
white: "#fff",
...
Pass them into your config for theme:
theme: {
colors,
. . .
Safelist your colors:
safelist: Object.keys(colors).map(color => `text-${color}`),
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>
</>
);
}
}
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;
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
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}
/>