I am styling my react component with styled-components. I want an icon component that can be used in different places just by changing size, colour props etc. I also want to pass icons names as props for different places. I am succeeded to change the size and colour but don't know how to pass the icon name as per requirement.
Here is my generic icon component:
import React from "react";
import { ReactSVG } from "react-svg";
import styled, { css } from "styled-components";
import { FaUserTie } from 'react-icons/fa';
const StyledSVGIcon = styled(FaUserTie)`
svg {
fill: black;
${({ size }) =>
size &&
css`
width: ${size};
height: ${size};
`}
${({ transform }) =>
transform &&
css`
transform: ${transform};
`}
path {
${({ color }) =>
color &&
css`
fill: ${color};
`}
}
}
`;
const GenIcon = props => {
return (
<StyledSVGIcon
src={`/icons/${props.name}.svg`}
color={props.color}
size={props.size}
transform={props.transform}
/>
);
};
export default GenIcon;
And I want to use it like this:
<GenIcon
name="FaUserNurse"
color="red"
size="16px"
/>
But the GenIcon component is not working. please help me where I am doing wrong. the icon could be any kind like svg or any other react icon library.
Try this out, you're close:
import React from "react";
import { ReactSVG } from "react-svg";
import styled, { css } from "styled-components";
import { FaUserTie, FaDocker } from "react-icons/fa";
const IconStyler = styled.span`
color: ${(props) => props.color};
& svg {
${(props) =>
props.small &&
css`
width: 14px !important;
height: 14px !important;
`}
${(props) =>
props.med &&
css`
width: 20px !important;
height: 20px !important;
`}
${(props) =>
props.large &&
css`
width: 28px !important;
height: 28px !important;
`}
}
`;
const Icon = ({ children, ...props }) => {
return <IconStyler {...props}>{children}</IconStyler>;
};
const GenIcon = () => {
return (
<div>
<h5>Any Icon</h5>
<div>
<Icon color="blue" small>
<FaUserTie />
</Icon>
</div>
<div>
<Icon color="orange" large>
<FaDocker />
</Icon>
</div>
</div>
);
};
export default GenIcon;
Here's a sandbox link: https://codesandbox.io/s/flamboyant-allen-60ho1?file=/src/GenIcon.js
NavStyles.js
import styled from 'styled-components';
export const Nav = styled.navwidth: 100%; ;
export const NavMenuMobile = styled.ul`
height: 80px;
.navbar_list_class {
font-size: 2rem;
background-color: red;
}
${props => props.navbar_list_props && `
font-size: 2rem;
background-color: gray;
`}
`;
Navbar.js
import React from 'react'
import {Nav, NavMenuMobile} from "./NavStyles";
const Navbar = () => {
return (
<Nav>
{/* work no problem */}
<NavMenuMobile navbar_list_props>Nav Bar props</NavMenuMobile>
{/* not work How to use..? */}
<NavMenuMobile className="navbar_list_class">Nav Bar class</NavMenuMobile>
</Nav>
)
}
export default Navbar
<Nav>
<NavMenuMobile className={navbar_list_props}>Nav Bar props</NavMenuMobile>
</Nav>
Try This
Looks like you are setting styles for the children within NavMenuMobile with the class "navbar_list_class".
Should work with &.navbar_list_class
export const NavMenuMobile = styled.ul`
height: 80px;
&.navbar_list_class {
font-size: 2rem;
background-color: red;
}
`;
I am using create-react-app, and im using a Data.js file where i have object with properties which i spread as props in a tag. But when i run npm start, or deploy my image wont show, and it looks like the compiler puts my video and images in a static/media directory. i tried webpack, file-loader en url-loader but no luck.
home.js
import React, { useState } from 'react'
import Sidebar from '../components/SideBar'
import NavBar from '../components/NavBar'
import HeroSection from '../components/HeroSection'
import InfoSection from '../components/InfoSection'
import { homeObjOne, homeObjTwo, homeObjThree } from '../components/InfoSection/Data'
//set sidebar navbar toggle states
//default state toggle is
const Home = () => {
const [isOpen, setIsOpen] = useState(false)
const toggle = () => {
setIsOpen(!isOpen)
}
return (
<>
<Sidebar isOpen={isOpen} toggle={toggle} />
<NavBar toggle={toggle} />
<HeroSection />
<InfoSection {...homeObjOne} />
<InfoSection {...homeObjTwo} />
<InfoSection {...homeObjThree} />
</>
)
}
export default Home
Data.js
import reactImage from '../assets/images/react.svg'
import reactImg2 from '../assets/images/draw_and_publish.png'
export const homeObjOne = {
id: 'about',
lightBg: false,
lightText: false,
lightTextDescription: true,
topLine: 'Create SVGs for your configurators easily',
headLine: 'With the Map Tool',
description: 'Quickly and easily draw available lot using the drawing tool',
buttonLabel: 'Try it for free >',
imgStart: false,
img: require('../assets/images/react.svg'),
alt: 'Drawing',
dark: true,
primary: true,
darkText: false,
}
export const homeObjTwo = {
id: 'usage',
lightBg: true,
lightText: false,
lightTextDescription: false,
topLine: 'Usage is very easy, use these four steps to ',
headLine: 'create your first project',
description: 'Get to know the app easily in these 4 simple steps',
buttonLabel: 'Try it for free >',
imgStart: true,
img: {reactImage},
alt: 'Steps',
dark: true,
primary: true,
darkText: false,
}
export const homeObjThree = {
id: 'publish',
lightBg: false,
lightText: false,
lightTextDescription: true,
topLine: 'Publish your drawings',
headLine: 'as SVG or as an Iframe',
description: 'and import it into your projects',
buttonLabel: 'Try it for free >',
imgStart: false,
img: {reactImg2},
alt: 'Publish',
dark: true,
primary: true,
darkText: false,
}
Index.js
import React from 'react'
import { InfoContainer, InfoWrapper, InfoRow, Column1, Column2, TextWrapper, TopLine, Heading, Subtitle, BtnWrap, ImgWrap, Img } from './InfoElements'
import { Button } from '../ButtonElement'
// import reactImage from '../assets/images/react.svg'
//n
const InfoSection = ({ lightBg, id, imgStart, topLine, lightText, lightTextDescription, darkText, headLine, description, buttonLabel, img, alt, primary, dark, dark2 }) => {
return (
<>
<InfoContainer lightBg={lightBg} id={id}>
<InfoWrapper>
<InfoRow imgStart={imgStart}>
<Column1>
<TextWrapper>
<TopLine>
{/* Create SVGs for your configurators easily */}
{topLine}
</TopLine>
<Heading lightText={lightText}>
{/* With the Map Tool */}
{headLine}
</Heading>
<Subtitle darkText={darkText} lightTextDescription={lightTextDescription}>
{/* Quickly and easily draw available lot using the drawing tool */}
{description}
</Subtitle>
<BtnWrap>
<Button to='signup'
smooth={true}
duration={500}
spy={true}
exact="true"
offset={-80}
primary={primary ? 1 : 0}
dark={dark ? 1 : 0}
dark2={dark2 ? 1 : 0}>
{/* Try it for free */}
{buttonLabel}
</Button>
</BtnWrap>
</TextWrapper>
</Column1>
<Column2>
<ImgWrap>
<Img src={img} alt={alt} />
</ImgWrap>
</Column2>
</InfoRow>
</InfoWrapper>
</InfoContainer>
</>
)
}
export default InfoSection
InfoElements (styled components)
import styled from 'styled-components'
// import { homeObjOne } from './Data'
//$ variables dependant on Data.js property values
export const InfoContainer = styled.div`
color: #fff;
background: ${({ lightBg }) => (lightBg ? '#f9f9f9' : '#d3d3d3')};
font-family: 'Lato', sans-serif;
#media screen and (max-width: 768px) {
padding: 100px 0;
}
`
export const InfoWrapper = styled.div`
display: grid;
z-index: 1;
height: 860px;
width: 100%;
max-width: 1100px;
margin-right: auto;
margin-left: auto;
padding: 0 24px;
justify-content: center;
`
export const InfoRow = styled.div`
display: grid;
grid-auto-columns: minmax(auto, 1fr);
align-items: center;
grid-template-areas: ${({ imgStart }) => (imgStart ? `'col2 col1'` : `'col1 col2'`)};
#media screen and (max-width: 768px) {
grid-template-areas: ${({ imgStart }) => (imgStart ? `'col1' 'col2'` : `'col1 col1' 'col2 col2'`)};
}
`
export const Column1 = styled.div`
margin-bottom: 15px;
padding: 0 15px;
grid-area: col1;
`
export const Column2 = styled.div`
margin-bottom: 15px;
padding: 0 15px;
grid-area: col2;
`
export const TextWrapper = styled.div`
max-width: 540px;
padding-top: 0;
padding-bottom: 60px;
`
export const TopLine = styled.p`
color: #393939;
font-size: 16px;
line-height: 16px;
font-weight: 700;
letter-spacing: 1.4px;
text-transform: uppercase;
margin-bottom: 16px;
`
export const Heading = styled.h1`
margin-bottom: 24px;
font-size: 48px;
line-height: 1.1;
font-weight: 600;
color: ${({ lightText }) => (lightText ? '#f7f8fa' : '#000000')};
#media screen and (max-width: 480px) {
font-size: 32px;
}
`
export const Subtitle = styled.div`
max-width: 440px;
margin-bottom: 35px;
font-size: 18px;
font-family: 'Lato', sans-serif;
line-height: 24px;
color: ${({ darkText }) => (darkText ? '#ffffff' : '#000000')};
`
export const BtnWrap = styled.div`
display: flex;
justify-content: flex-start;
`
export const ImgWrap = styled.div`
max-width: 555px;
height: 100%;
`
export const Img = styled.div`
width: 100%;
margin: 0 0 10px 0;
padding: 0;
/* background-image: url('../images/react.svg'); */
`
**you img is a img component not a div component so rename its with img not a styled.div **
I understand that styles can be conditionally rendered such as:
const HelloWorldLabel= styled("div")<{ centered?: boolean }>`
display: ${({ centered }) => (centered ? "block" : "flex")};;
margin: ${({ centered }) => (centered ? "auto 0" : "unset")};
padding: ${({ centered }) => (centered ? "0 15px" : "unset")};
`;
This does not look DRY - How can I (is it possible) render an entire block of css styles based on props?
Something like:
const HelloWorldLabel= styled("div")<{ centered?: boolean }>`
if (centered) {
display: "block" ;
margin: $"auto 0";
padding: "0 15px" ;
} else {
......
}
`;
With styled-component, or any CSS-in-JS, you can conditionally render a css block:
import styled, { css } from 'styled-components';
const light = css`
background-color: white;
color: black;
`;
const dark = css`
background-color: black;
color: white;
`;
const Box = styled.div`
${({ isDark }) => (isDark ? light : dark)}
`;
Full Example:
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import styled, { css } from 'styled-components';
const light = css`
background-color: white;
border: 2px solid black;
color: black;
`;
const dark = css`
background-color: black;
color: white;
`;
const FlexBox = styled.div`
margin: 20px;
padding: 20px;
${({ isDark }) => (isDark ? light : dark)}
`;
const App = () => {
const [isDark, setIsDark] = useState(false);
const toggle = () => setIsDark(b => !b);
return (
<FlexBox isDark={isDark}>
<div>Some Text</div>
<button onClick={toggle}>Change Block</button>
</FlexBox>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
A less verbose way that worked for me is
const SideMenu = styled.aside`
width: 200px;
${({ isHidden }) => isHidden && `
display: none;
`}
// another random prop you need here
${({ redBg }) => redBg && `
background-color: red;
`}
`;
You can use a function and return the css based on prop:
const HelloWorldLabel= styled("div")`
${({centered}) => {
if (centered) {
return `
display: "block" ;
margin: "auto 0";
padding: "0 15px";
`
} else {
return `// Other styles here`
}
}}
`;
The alternative is
let customCss = setCustomCss(position) => {
let positionCss = {
center: [ 'css: value;', 'css:value;'],
left: .....
right: ....
}
return return positionCss[position];
}
let HelloWorldLabel= styled('div')(customCss, {
/* css common to all */
})
I have button.js:
import React from "react";
import styled from "styled-components";
const StyledButton = styled.TouchableOpacity`
border: 1px solid #fff;
border-radius: 10px;
padding-horizontal: 10px;
padding-vertical: 5px;
`;
const StyledButtonText = styled.Text`
color: #fff;
font-size: 12;
`;
export default ({ children }) => (
<StyledButton>
<StyledButtonText>{children.toUpperCase()}</StyledButtonText>
</StyledButton>
);
And its usage:
import React, { Component } from "react";
import styled from "styled-components";
import Button from "./button";
const StyledNavView = styled.View`
justify-content: flex-end;
flex-direction: row;
background: #000;
padding-horizontal: 10px;
padding-vertical: 10px;
`;
const StyledTodayButton = styled(Button)`
margin: 10px;
`;
export default class Nav extends Component {
render() {
return (
<StyledNavView>
<StyledTodayButton>Today</StyledTodayButton>
<Button>Previous</Button>
</StyledNavView>
);
}
}
Problem is, the margin I apply in StyledTodayButton is actually never applied. Have I misunderstood extending styles in Styled Components?
There are 2 ways to make it work:
extend button style:
const StyledTodayButton = Button.extend'margin: 10px'
pass prop to button:
const Button = styled.button'
/* ...your props */
margin: ${props => props.withMargin ? '10px' : '0px'};
and then call in render method you can invoke it with:
<Button withMargin {...restProps} />