'React — Passing props with styled-components

I just read in the styled-components documentation that the following is wrong and it will affect render times. If that is the case, how can I refactor the code and use the required props to create a dynamic style?

Thank you in advance.

Tab component

import React from 'react'
import styled from 'styled-components'

const Tab = ({ onClick, isSelected, children }) => {
    const TabWrapper = styled.li`
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 100px;
    margin: 1px;
    font-size: 3em;
    color: ${props => (isSelected ? `white` : `black`)};
    background-color: ${props => (isSelected ? `black` : `#C4C4C4`)};
    cursor: ${props => (isSelected ? 'default' : `pointer`)};
`

    return <TabWrapper onClick={onClick}>{children}</TabWrapper>
}


export default Tab


Solution 1:[1]

I believe what the documentation is saying is that you should avoid including your styles inside of the rendering component:

DO THIS

const StyledWrapper = styled.div`
  /* ... */
`

const Wrapper = ({ message }) => {
  return <StyledWrapper>{message}</StyledWrapper>
}

INSTEAD OF THIS

const Wrapper = ({ message }) => {
  // WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!!
  const StyledWrapper = styled.div`
    /* ... */
  `

  return <StyledWrapper>{message}</StyledWrapper>
}

Because what happens is when the component's Props changes, then the component will re-render and the style will regenerate. Therefore it makes sense to keep it separate.

So if you read further on to the Adapting based on props section, they explain this:

const Button = styled.button`
  /* Adapt the colours based on primary prop */
  background: ${props => props.primary ? "palevioletred" : "white"};
  color: ${props => props.primary ? "white" : "palevioletred"};

  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

// class X extends React.Component {
//  ...

render(
  <div>
    <Button>Normal</Button>
    <Button primary>Primary</Button>
  </div>
);

// }

this works because when you use the Button component in class X, it will know the props of class X without you having to tell it anything.

For your scenario, I imagine the solution would be simply:

const TabWrapper = styled.li`
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 100px;
  margin: 1px;
  font-size: 3em;
  color: ${props => (props.isSelected ? `white` : `black`)};
  background-color: ${props => (props.isSelected ? `black` : `#C4C4C4`)};
  cursor: ${props => (props.isSelected ? 'default' : `pointer`)};
`;

const Tab = ({ onClick, isSelected, children }) => {
  return <TabWrapper onClick={onClick}>{children}</TabWrapper>
}

const X = <Tab onClick={() => console.log('clicked')} isSelected>Some Children</Tab>

I haven't tested this at all, so please feel free to try it out and let me know if it works for you or whatever worked for you!

Solution 2:[2]

You can pass an argument with Typescript as follows:

<StyledPaper open={open} />    

...

const StyledPaper = styled(Paper)<{ open: boolean }>`
   top: ${p => (p.open ? 0 : 100)}%;
`;

Solution 3:[3]

Another way to do it would be

const StyledDiv = styled.div.attrs((props: {color: string}) => props)`
    width: 100%;
    height: 100%;
    background-color: ${(props) => props.color};
`

//...

render() {
    return (
        <StyledDiv color="black">content...</StyledDiv>
    );
}

This way you are type-safe in terms of the props you want to send into the styled component. (Good when coding in Typescript)

Solution 4:[4]

For a more simple example with functional components:

Suppose you have an arrow like polygon and you need 2 of them pointing in different directions. So you can pass the rotate value by props

               <Arrow rotates='none'/>
          
               <Arrow rotates='180deg'/>

Then in the Component Arrow you have to pass the props like normal component to the styled component but in the styled component you have to use it like props:

    import React from 'react';
    import styled from "@emotion/styled";

    const ArrowStyled = styled.div`
      background-color: rgba(255,255,255,0.9);
      width: 24px;
      height: 30px;
      clip-path: polygon(56% 40%,40% 50%,55% 63%,55% 93%,0% 50%,56% 9%);
      transform: rotate(${props => props.rotates});
     `


const Arrow = ({rotates}) => {
    return (
       <ArrowStyled rotates={rotates}/>
    );
}

export default Arrow;
           

Solution 5:[5]

If you're using Typescript create an interface inside your styles file! Otherwise, you won't be able to access props in your CSS

import styled from 'styled-components'

interface StyledLiProps{
  selected: boolean
}

export const TabWrapper = styled.li`
    
// styles ...

color: ${props => (selected ? `white` : `black`)};
background-color: ${props => (selected ? `black` : `#C4C4C4`)};
`

And don`t forget to declare the props you want to use in your CSS inside your JSX

interface TabProps{
  text: string;
}

const Tab = ({ text }: TabProps) => {
 
//...
  
return <TabWrapper selected={isSelected} onClick={() => updateTab}>{text}</TabWrapper>


}

Solution 6:[6]

Consider styled components documentation gives example of using reacts context api [2] for different themes.

[1] https://www.styled-components.com/docs/advanced

[2] https://reactjs.org/docs/context.html

Solution 7:[7]

Exporting styled-component

Button

and passing scrollPosition as a prop in functional component

PassingPropsToSyledComponent

import styledComponents from "styled-components";
            
export const Button = styledComponents.div`
       position: ${ props => props.scrollPosition ? 'relative' : 'static' };
`;
export const PassingPropsToSyledComponent = ()=> {
   return(
   <Button scrollPosition={scrollPosition}>
       Your Text Here
   </Button>
   )
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 dropbeardan
Solution 2 Jöcker
Solution 3 Roy Shilkrot
Solution 4 galo hernandez
Solution 5
Solution 6 Cisum Inas
Solution 7 Muhammad Usman