I use styled-components each component from styled-components I pass in other components, in order to apply them, the problem is that my code looks ugly because every component style I pass in other components it looks like this
SideBarStyledComponents.js
export default function SideBarStyledComponents(props) {
const {SideBarValue} = React.useContext(CounterContext);
const [SideBarThemeValue] = SideBarValue;
const PageColor = SideBarThemeValue && SideBarThemeValue.PageContentColor;
const AlertBg = SideBarThemeValue && SideBarThemeValue.AlertBackground;
const LessonContainers = styled.div`
margin: 2rem 0 2rem 0;
`;
const LessonSideBarTitle = styled.h1`
font-size: 1.8rem;
font-weight: 500;
color: ${(PageColor ? PageColor : "#2c3e50")};
font-family: 'Roboto';
margin-top: 1rem;
`;
return(
<RoutesPage {...props} LessonContainers={LessonContainers} SideBarThemeValue={SideBarThemeValue}
LessonSideBarTitle={LessonSideBarTitle}/>
);
}
RoutesPage.js
function RoutesPage(props) {
const {path} = props.path;
const routes = [
{
path: `${path}/Introduction`,
component: () => <Introduction {...props} />
},
{
path: `${path}/Creating Your First Javascript`,
exact: true,
component: () => <CreatingFirstJS {...props} />
},
{
path: `${path}/Guardian`,
component: () => <h2>Shoelaces</h2>
}
];
return (
<>
<Switch>
{routes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</>
);
}
Please pay attention, you have noticed every style I pass to the components and so every time I create a new component every style I have to pass this way I will have many components since I am creating a sidebar I want to know if there is a way to get rid of this
You should define all the styled components outside in a separate file (or multiple files). And then you should import those styled components directly within your component where you are going to use it.
Passing them as props is a bad practice.
For example you can create a file called 'StyledComponents.js' and export all your styled components.
...
export const LessonContainers = styled.div`
margin: 2rem 0 2rem 0;
`;
export const LessonSideBarTitle = ({children}) => {
const {SideBarValue} = React.useContext(CounterContext);
const [SideBarThemeValue] = SideBarValue;
const PageColor = SideBarThemeValue && SideBarThemeValue.PageContentColor;
const H1 = styled.h1`
font-size: 1.8rem;
font-weight: 500;
color: ${(PageColor ? PageColor : "#2c3e50")};
font-family: 'Roboto';
margin-top: 1rem;
`;
return <H1>{children}</H1>
}
...
And now in the Introduction or CreatingFirstJS component, you can just import the necessary styled components like so:
import {LessonSideBarTitle} from 'path/to/StyledComponents';
Another way is to take advantage of the styled object properties, to remove the ugly long interpolation of props by destructuring the props
import styled from "styled-components"
const Button = styled.button(
({ bgColor, fontColor }) => `
background: ${bgColor};
color: ${fontColor};
`
);
function App() {
return (
<Button bgColor="#000" fontColor="#fff"> Hello World </Button>
)
}
Example in codesandbox
Related
I've been searching about this error a while but couldn't find a solution...
I'm using styled components and ant.design.
Button Component
import React from 'react'
import {Btn} from './style';
const ComponentButton = (props) =>{
const {title, backgroundColor,color, hoverColor, handleClick,shape} = props
return(
<Btn
shape={shape || "round"}
onClick={handleClick}
backgroundColor={backgroundColor}
color={color}
hoverColor={hoverColor}
>
{title}
</Btn>
)
}
export default ComponentButton;
styled-Component
import styled, {css} from 'styled-components';
import {primaryColor, white} from '../../../../config';
import { Button } from 'antd';
export const Btn = styled(Button)`
${(props, {color, backgroundColor, hoverColor, width} = props) =>
css`
color: ${color ? color : white};
background-color: ${backgroundColor ? backgroundColor : primaryColor} !important;
width: ${`${width}px` ? `${width}px` : '-webkit-fill-available'};
border: none !important;
&:hover, &:focus{
color: ${hoverColor ? hoverColor : white};
border: none !important;
}
&:after{
box-shadow: none !important;
}
`}
`
I don't know why I still getting this error:
React does not recognize the backgroundColor prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase backgroundcolor instead.
styled-components will automatically add all props into DOM element by default, such as:
<button backgroundColor="" color="" hoverColor="" ... />
and react will check the props of the DOM element are legal.
also, this line ${(props, {color, backgroundColor, hoverColor, width} = props) looks a little weird, this should only have one parameter.
you can try this:
// avoid pass all props into button element
export const Btn = styled(({color, backgroundColor, hoverColor, width, ...props}) => <Button {...props} />)`
${(p = props) =>
css`
color: ${p.color ? p.color : white};
background-color: ${p.backgroundColor ? p.backgroundColor : primaryColor} !important;
width: ${`${p.width}px` ? `${p.width}px` : '-webkit-fill-available'};
border: none !important;
&:hover, &:focus{
color: ${p.hoverColor ? p.hoverColor : white};
border: none !important;
}
&:after{
box-shadow: none !important;
}
`}
`
In case you arrive here because of the error message in the title: Here's a solution for the problem in the world of react and javascript:
/**
* This layer of *styled* will make the error go away
**/
const StylableButton = styled.button({}, (props) => ({
...props,
// Just in case you'd use typescript:
//...(props as any),
}));
/**
* Your custom button component
**/
const MyButton= (props) => {
return <StylableButton {...props} />;
};
Using it like this:
const MyStyledButton = styled(MyButton)({
backgroundColor: 'red',
})
Code for your case
Here is my folder structure:
type/
- Heading.js
- index.js
card/
- Card.js
- CardGroup.js
- index.js
JSX:
<CardGroup>
<Heading>...</Heading>
<Card>...</Card<
</CardHeading>
Now I am trying to style the Heading and Card components differently if they are nested inside a CardGroup. CardGroup.js:
import Heading from '../type';
import Card from './Card';
const CardGroup = styled.div`
${Heading} { ... }
${Card} { ... }
`;
Works OK for the Heading but NOT for the Card. I came accross this issue before and I can't wrap my head around what's causing this. Is it because the are in the same folder? Is it because of the order they are imported in my app? Any ideas would be really helpful.
Updated:
My Card.js implementation:
const StyledCard = styled.div`...`;
const Card = props => {
...
<StyledCard {...props}>{props.children}</StyledCard>
}
You need to target the component generated by styled-component (StyledCard in your example).
// Card.js
const ContainerCard = styled.div`
width: 50px;
height: 50px;
`;
const Card = ({ className }) => {
return <ContainerCard className={className} />;
};
// Use any valid prop, Card.StyledComponent, Card.Style etc.
Card.className = ContainerCard;
export default Card;
// App.js
const Container = styled.div`
height: 100vh;
width: 100vw;
`;
// Styling custom components, through className prop.
const VioletredRedCard = styled(Card)`
background-color: palevioletred;
`;
// Target the component generated by styled-components
const CardWrapper = styled.div`
${Card.className} {
width: 100px;
height: 100px;
background-color: paleturquoise;
}
`;
const App = () => {
return (
<Container>
<CardWrapper>
<Card />
</CardWrapper>
<VioletredRedCard />
</Container>
);
};
And, of course, if you want to style the card via: styled(Card), be sure you pass the className prop like in the example above.
You can use classname attr with styled components.
const Card = styled.div.attrs({
className:"card"
})`
card styles
`
and you can use that className in CardGroup
const CardGroup=styled.div`
& .card{}
`
Total newbie on using styled-components. I'm wondering what's the usage of it? How should I implement component life cycle methods after styling it? For simplicity sake I've removed all the other style.
import styled from 'styled-components';
const Button = styled.button`
background-color: 'green'
`
export default Button;
I'm wondering how do I further working on this Button component?
Traditionally we can declare a class-based component and implement some lifecycle methods, but now with this styled-components, I'm not really sure how to combine them together as they are really the single Button Component?
UPDATES:
Full sourcecode for Button.js. By having the below code, all styles will be gone and I can't understand the problem
import React from 'react';
import styled from 'styled-components';
// import Button from 'react-bootstrap/Button';
import color from '../config/color';
const Button = ({ children, onPress }) => (
<button type="button" onPress={onPress}>{children}</button>
);
const StyledButton = styled(Button)`
width: 12rem;
height: 54px;
font-size: 1rem;
background-color: ${(props) => {
if (props.inverted) return 'white';
if (props.disabled) return color.disabled;
return (props.color || color.primary);
}};
color: ${(props) => {
if (props.disabled) return color.disabledText;
if (props.inverted) return (props.color || color.primary);
return 'white';
}};
border:${(props) => (props.inverted ? `2px solid ${props.color || color.primary}` : 'none')};
border-radius: 60px;
&:hover {
filter: ${(props) => (props.inverted || props.disabled ? 'none' : 'brightness(95%)')}
}
`;
export default StyledButton;
In order to style a custom react component you can pass on the custom component name as argument to styled. According to the doc:
The styled method works perfectly on all of your own or any
third-party component, as long as they attach the passed className
prop to a DOM element.
import React from 'react';
import styled from 'styled-components';
// import Button from 'react-bootstrap/Button';
import color from '../config/color';
const Button = ({ children, className onPress }) => (
<button type="button" className={className} onPress={onPress}>{children}</button>
);
const StyledButton = styled(Button)`
width: 12rem;
height: 54px;
font-size: 1rem;
background-color: ${(props) => {
if (props.inverted) return 'white';
if (props.disabled) return color.disabled;
return (props.color || color.primary);
}};
color: ${(props) => {
if (props.disabled) return color.disabledText;
if (props.inverted) return (props.color || color.primary);
return 'white';
}};
border:${(props) => (props.inverted ? `2px solid ${props.color || color.primary}` : 'none')};
border-radius: 60px;
&:hover {
filter: ${(props) => (props.inverted || props.disabled ? 'none' : 'brightness(95%)')}
}
`;
export default StyledButton;
Read the styled-component documentation for more details on styling any component
Let's rename the styled button component to reduce confusion between the 2 similarly named components.
styled-button.tsx:
import styled from 'styled-components';
const StyledButton = styled.button`
background-color: 'green'
`
export default StyledButton;
When you import the styled button component into your Button component, you can actually use make use of it the way you usually do when you are working with traditional HTML <button> elements, as its props are exposed and available on the styled component as well.
button.tsx:
import StyledButton from './StyledButton'
class Button extends React.Component {
componentDidMount() {
const { someProps, otherProps } = this.props;
// some lifecycle logic
}
handleClick() {
// do the rest
}
render() {
return <StyledButton onClick={() = this.handleClick()} />;
}
}
If you want, you can even pass in props from the parent Button component, to the child StyledButton component. This will allow you to customise it.
render() {
const { color } = this.props;
return <StyledButton background={color} onClick={() = this.handleClick()} />;
}
And on your StyledButton component, you just need to make the following changes:
const StyledButton = styled.button`
background-color: ${({ color }) => color || 'green'}
`
What other answers lack is for styling custom components like Button you have to pass a className prop thought it.
The styling is injected through className property.
const ButtonDefaultStyle = styled.button`
width: 5rem;
`;
const Button = ({ className, children, onPress }) => (
<ButtonDefaultStyle className={className} type="button" onPress={onPress}>
{children}
</ButtonDefaultStyle>
);
export default Button;
Then the styles can be applied:
import Button from './Button.js'
// Will override width: 5rem;
const StyledButton = styled(Button)`
width: 12rem;
`;
I have 3 components in 3 differents files :
//file1
const Icon1 = styled.div`
width: 10px;
`;
const Widget1 = () => <BaseWidget icon={<Icon1 />} />
//file2
const Icon2 = styled.div`
width: 15px;
`;
const Widget2 = () => <BaseWidget icon={<Icon2 />} />
//file3
const Icon3 = styled.div`
width: 20px;
`;
const Widget3 = () => <BaseWidget icon={<Icon3 />} />
and my base widget :
//basewidget
const BaseWidget = (props) => <div>{props.icon}</div>
My question is : how can I add style to props.icon in basewidget ?
Can I create a styled component based on props.icon and add a common css property ?
If it's not possible what is the best solution ?
Thanks,
Jef
When you are passing in icon={<Icon2 />} you are actually passing a JSX element, which cannot be styled from the other side with styled-components because it is not a component. To style a component, it needs to take the className as a prop. In this case, your best bet is to write a styled-component wrapper dev and let the styles cascade to your {props.icon}
But because you are not passing in any props to Icon you could easily pass in the component as a prop making it stylable.
<BaseWidget icon={Icon1} />
Where you are receiving it:
import styled from "styled-components";
const StyledIcon = styled.i``;
const BaseWidget = (props) => {
const { Icon } = props.icon;
return (
<StyledIcon as={Icon} />
);
}
as Docs
I am trying to render an image. Here's my code:
Accordion.jsx
import React from 'react';
import ArrowTemplate from "./ArrowTemplate";
function Accordion() {
return (
<div>
<ArrowTemplate arrowType={"BlackDown"} styles={""}/>
{/*<ArrowTemplate arrowType={"BlackUp"}/>*/}
{/*<ArrowTemplate arrowType={"WhiteDown"} styles={"background:black"}/>*/}
{/*<ArrowTemplate arrowType={"WhiteUp"} styles={"background:black"}/>*/}
</div>
);
}
export default Accordion;
ArrowTemplate.jsx
import BlackDownArrowSVG from './svgs/black-down-arrow.svg';
import WhiteDownArrowSVG from './svgs/white-down-arrow.svg';
import styled from 'styled-components';
import PropTypes from 'prop-types';
ArrowTemplate.propTypes = {
arrowType: PropTypes.string,
styles: PropTypes.string,
};
function ArrowTemplate(props) {
const {arrowType, styles} = props;
switch (arrowType) {
case "WhiteDown":
return (
styled.img.attrs({
src: WhiteDownArrowSVG,
})`${styles !== null ? styles : ""}`
);
case "BlackDown":
return (
styled.img.attrs({
src: BlackDownArrowSVG,
})`${styles !== null ? styles : ""}`
);
case "WhiteUp":
return (
styled.img.attrs({
src: WhiteDownArrowSVG,
})`transform: rotate(180deg); ${styles !== null ? styles : ""}`
);
case "BlackUp":
return (
styled.img.attrs({
src: BlackDownArrowSVG,
})`transform: rotate(180deg); ${styles !== null ? styles : ""}`
);
default:
throw("You need to pass arrowType");
}
}
export default ArrowTemplate;
The SVG paths are correct.
As the error I get this:
Objects are not valid as a React child (found: object with keys {$$typeof, render, displayName, attrs, componentStyle, foldedComponentIds, styledComponentId, target, withComponent, warnTooManyClasses, toString}). If you meant to render a collection of children, use an array instead.
in ArrowTemplate (at Accordion.jsx:7)
When I console.log I get a long object. But I get a similar object when I console.log the example code from the docs:
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
//^^^^^^^^^^^^^^^^^^^^^
//This renders with no problem
Where am I going wrong here?
If you come across this when declaring a styled component, you could be forgetting the template literals at the end of the declaration. So
const AppContentWithSideBar = styled((props) => {
return (
<Wrapper>
<Router>
<SideBarWrapper>
<SideBar />
</SideBarWrapper>
<MainWrapper>
{props.children}
</MainWrapper>
</Router>
</Wrapper>
);
});
should be
const AppContentWithSideBar = styled((props) => {
return (
<Wrapper>
<Router>
<SideBarWrapper>
<SideBar />
</SideBarWrapper>
<MainWrapper>
{props.children}
</MainWrapper>
</Router>
</Wrapper>
);
})``;
Try:
import BlackDownArrowSVG from './svgs/black-down-arrow.svg';
import WhiteDownArrowSVG from './svgs/white-down-arrow.svg';
import styled from 'styled-components';
import PropTypes from 'prop-types';
ArrowTemplate.propTypes = {
arrowType: PropTypes.string,
styles: PropTypes.string,
};
function ArrowTemplate(props) {
const {arrowType, styles} = props;
let StyledArrowTemplate;
switch (arrowType) {
case "WhiteDown":
StyledArrowTemplate = (
styled.img.attrs({
src: WhiteDownArrowSVG,
})`${styles !== null ? styles : ""}`
);
break;
case "BlackDown":
StyledArrowTemplate = (
styled.img.attrs({
src: BlackDownArrowSVG,
})`${styles !== null ? styles : ""}`
);
break;
case "WhiteUp":
StyledArrowTemplate = (
styled.img.attrs({
src: WhiteDownArrowSVG,
})`transform: rotate(180deg); ${styles !== null ? styles : ""}`
);
break;
case "BlackUp":
StyledArrowTemplate = (
styled.img.attrs({
src: BlackDownArrowSVG,
})`transform: rotate(180deg); ${styles !== null ? styles : ""}`
);
break;
default:
throw("You need to pass arrowType");
}
return <StyledArrowTemplate />;
}
export default ArrowTemplate;
EDIT:
Apologies for the lack of initial lack of explanation!
So the reason this error occurs is when you return some sort of Object in your JSX code. In this case it is styled.img.attrs. So to get around this, we declare a variable which will then be set to be the styled component inside one of the cases, depending on what props you provide to the ArrowTemplate component, and return it at the end of the function.
This way, you are basically creating a StyledComponent as you normally would, but in a dynamic way.
Lindsay's above answer probably works fine, but I think it would make even more sense not to have a switch case, but to return a single component and pass in your conditions as props, doing your conditional logic in the definition of the styled component, i.e. something like...
const ArrowTemplate = styled.div`
src: ${props => props.color === Black ? BlackDownArrowSVG : WhiteDownArrowSVG};
${props => props.direction === Down ? transform: rotate(180) : null }
etc...
`;
(not sure I have the above syntax exactly right, but this is the basic idea)
and
<ArrowTemplate color={Black} direction={Up} src={src} />
const Button = styled.button`
font: inherit;
padding: 0.5rem 1.5rem;
border: 1px solid #8b005d;
color: white;
background: #8b005d;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.26);
cursor: pointer;
`
You are getting this error because you are storing the value in the function instead of the variable. let me give you an example and let you know.
This will work for you.