Multiple Props Options for Styled Components
Asked Answered
C

7

15

I have a navbar component that I have created using Styled Components. I would like to create some props that change the background-color and/or text color.

For instance: <Navbar dark> should have the following CSS:

background: #454545;
color: #fafafa;

Whereas <Navbar light> should be the opposite:

background: #fafafa;
color: #454545;

If, however, neither prop is used, then I want to have a default background and text color -- say (for demo purposes), something like this:

background: #eee;
color: #333;

Now, my question is how to set this up in Styled Components.

I can do the following:

background: ${props => props.dark ? #454545 : '#eee'}
background: ${props => props.dark ? #fafafa : '#eee'}
background:  #eee;

And something similar for color.

But this is redundant and not very elegant. I would like some sort of if/else statement:

background: ${ props => { 
  if (props.dark) { #454545 }
  elseif (props.light) { #fafafa }
  else { #eee }
}

But I don't know how to set something like that up in Styled Components.

Any suggestions?

Thanks in advance.

Catalonia answered 8/5, 2019 at 19:19 Comment(0)
C
18

This is the solution I ended up using:

export const Navbar = styled.nav`
  width: 100%;

  ...  // rest of the regular CSS code

  ${props => {
    if (props.dark) {
      return `
        background: ${colors.dark};
        color: ${colors.light};
    `
    } else if (props.light) {
      return `
        background: ${colors.light};
        color: ${colors.dark};
    `
    } else {
      return `
        background: ${colors.light};
        color: ${colors.dark};
    `
    }
  }}
`
Catalonia answered 12/5, 2019 at 9:53 Comment(1)
This is what Ive been looking for all this time.Suffragan
B
57

Keep the passed in prop name the same. Then you can utilize a switch/case statement. For example, passing in a color prop and using it as a type to be matched against a case.

Working example:

Edit Simple Styled Components


For example:

<Button color="primary">Example</Button>

components/Button

import styled from "styled-components";

const handleColorType = color => {
  switch (color) {
    case "primary":
      return "#03a9f3";
    case "danger":
      return "#f56342";
    default:
      return "#fff";
  }
};

const Button = styled.button`
  display: block;
  cursor: pointer;
  border: 0;
  margin: 5px 0;
  background: #000;
  font-size: 20px;
  color: ${({ color }) => handleColorType(color)};

  &:focus {
    outline: 0;
  }
`;

export default Button;

If you have multiple attributes (like a color and a background pair), then utilizing the same concept as above, alter the handleColorType to return a string with attributes and invoke the handleColorType function without a style property.

For example:

<MultiButton color="primary">Example</MultiButton>

components/MultiButton

import styled from "styled-components";

const handleColorType = color => {
  switch (color) {
    case "primary":
      return "color: #03a9f3; background: #000;";
    case "danger":
      return "color: #fff; background: #f56342;";
    default:
      return "color: #000; background: #eee;";
  }
};

const MultiButton = styled.button`
  display: block;
  margin: 5px 0;
  cursor: pointer;
  border: 0;
  font-size: 20px;
  ${({ color }) => handleColorType(color)};

  &:focus {
    outline: 0;
  }
`;

export default MultiButton;
Brownie answered 8/5, 2019 at 21:45 Comment(6)
This is very nice and elegant (and I definitely will use it at times). However, I also want a solution for when I do not want to pass a value to the props (such as the dark and light examples I gave above). Could I modify your solution as use an if/else statement to check the whether or not a prop is set -- i.e., something like if (props.dark) { return 'background-color: #454545; color: #fafafa' } elseif... . Would that work?Catalonia
Or, alternatively -- is it possible to use switch with with props and then check for the different types of props. Something like this: switch (props) { case props.dark...? I'm trying to get that working, but have gotten an error. Any ideas?Catalonia
The default of a switch/case returns something (or can be nothing) if the none of the cases match. See codesandbox example. Also, again keep the same prop name. Using "dark" and "light" as passed in props will force you into using a heavily nested if/else statement.Brownie
But, if that's what you absolutely want, then yes, all you need to add is a return statement.Brownie
Just fanboying this approach. A former co-worker introduced me to this and it's sooo much more elegant than any other approach I've used.Canady
Wonderful comment my friend !Galumph
C
18

This is the solution I ended up using:

export const Navbar = styled.nav`
  width: 100%;

  ...  // rest of the regular CSS code

  ${props => {
    if (props.dark) {
      return `
        background: ${colors.dark};
        color: ${colors.light};
    `
    } else if (props.light) {
      return `
        background: ${colors.light};
        color: ${colors.dark};
    `
    } else {
      return `
        background: ${colors.light};
        color: ${colors.dark};
    `
    }
  }}
`
Catalonia answered 12/5, 2019 at 9:53 Comment(1)
This is what Ive been looking for all this time.Suffragan
C
2

Something more elegant (I guess) and modern would be a combination of destructuring the props and using the switch statement such as:

const Button = styled.button`
  ${({primary, secondary}) => {
      switch(true) {
        case primary:
          return `background-color : green`
        case secondary:
          return `background-color : red`
      }
    }
  }
`
Chamberlain answered 7/4, 2020 at 1:34 Comment(0)
S
1

Styled components also accepts a function where in you can read props. Moreover if you choose to pass a theme prop instead, you can also define an object for your themes.


const themes = {
  dark: css `
     background: ${colors.dark};
     color: ${colors.light};
  `,
  light: css`
     background: ${colors.light};
     color: ${colors.dark};
  `
}
export const Navbar = styled.nav(({theme})=>`
  width: 100%;
  background: ${colors.light};
  color: ${colors.dark};
  ... // rest of the css

  ${theme?themes[theme]:''}

  `)

<Navbar theme="dark" />
Stier answered 2/5, 2020 at 14:29 Comment(0)
P
1

What about:

const StyledButton = styled.button`
    padding: 8px 16px;
    border-radius: ${props => props.rounded ? '10px' : '0px'};
    ${props => {
        switch (props.type) {
            case 'primary':
                return `
                    background-color : #000;
                    color: #fff;
                    border: 1px solid #000000;
                `;
            case 'secondary':
                return `
                    background-color : #DEDEDE;
                    border: 1px solid #999;
                `;
        }
    }
}`;
Precincts answered 24/2, 2022 at 2:27 Comment(0)
G
0

you can also do something like this:


 const colorType= {
   dark: '#454545',
   light: '#0a0a0a',
   normal: '#dedede'
};



export const Navbar= styled.nav`
   background: ${({color}) => colorType[color] || `${color}`};

`;

and Here you are :

<Navbar color="primary" />
<Navbar color="#FFFFFF" />

Giddings answered 28/2, 2020 at 18:6 Comment(0)
F
0

Solution using Styled-tools

In your specific case

Since you only got two themes, a default which is light, and a dark one you can switch to, you only really need to check if it is darkmode or not.

import {ifProp} from "styled-tools";

export const Navbar = styled.nav`
  ${ifProp("dark",
    css`
      background: ${colors.dark};
      color: ${colors.light};
    `,
    css`
      background: ${colors.light};
      color: ${colors.dark};
    `,

  )}
`;

and render with <Navbar $dark={isDarkMode} />

More general solution

Granted you still want to use a themed approach with dark/light etc. setting up variants would make things easier. For example you can keep track of the different themes you want in a separate enum. Like this:

enum Themes {
    DARK = "dark",
    LIGHT = "light",
}

In your styling later you could specify:

import {switchProp} from "styled-tools";

export const Navbar = styled.nav`
  ${switchProp({
    dark: css`
      background: ${colors.dark};
      color: ${colors.light};
    `,
    light: css`
      background: ${colors.light};
      color: ${colors.dark};
    `,

  })}
`;

and then rendering either <Navbar variant={Theme.LIGHT} /> or <Navbar variant={Theme.DARK} />

References

Finochio answered 9/12, 2021 at 12:40 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.