conditional rendering in styled components
Asked Answered
M

7

137

How can I use conditional rendering in styled-components to set my button class to active using styled-components in React?

In css I would do it similarly to this:

<button className={this.state.active && 'active'}
      onClick={ () => this.setState({active: !this.state.active}) }>Click me</button>

In styled components if I try to use '&&' in the classname it doesn't like it.

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

const Tab = styled.button`
  width: 100%;
  outline: 0;
  border: 0;
  height: 100%;
  justify-content: center;
  align-items: center;
  line-height: 0.2;
`

export default class Hello extends React.Component {
  constructor() {
    super()
    this.state = {
      active: false
    }  
    this.handleButton = this.handleButton.bind(this)
}

  handleButton() {
    this.setState({ active: true })
  }

  render() {
     return(
       <div>
         <Tab onClick={this.handleButton}></Tab>
       </div>
     )
  }}
Monty answered 29/1, 2018 at 13:40 Comment(0)
V
255

You can simply do this

<Tab active={this.state.active} onClick={this.handleButton}></Tab>

And in your styles something like this:

const Tab = styled.button`
  width: 100%;
  outline: 0;
  border: 0;
  height: 100%;
  justify-content: center;
  align-items: center;
  line-height: 0.2;

  ${({ active }) => active && `
    background: blue;
  `}
`;
Vernon answered 29/1, 2018 at 13:48 Comment(12)
Documentation: Adapting based on props.Menefee
In TypeScript you need to use either ({ active }: any) or withProps.Menefee
It adds a false in the CSS string and can cause troublesFarinose
@LucasWillems if you use an out of date babel transpiler and react it does. React doesn't render the falsy values anymoreBettiebettina
@JoãoCunha Okay, thank you, I didn't know that! I use the last version of Next.js and I have this issue... Do you know why? Here is their package.json: github.com/zeit/next.js/blob/canary/packages/next/package.jsonFarinose
Is this still working in 2020? I tried it and found not working and the link is posted by @MarcoLackovic is updated i guess. Any other way to do this now.Assimilation
@Assimilation Yeah it's still working for me. You might be seeing a different issue.Elegiac
The conditional code shouldn't be wrapped in plain backticks, but rather should be wrapped in css``Winson
What's your opinion on using classNames instead for conditionnal rendering? They demo it there: styled-components.com/docs/…. Using props feels like a fallback to me. It echoes the "Happy Path" from Josh Comeau: joshwcomeau.com/css/styled-components, the article proposes to use CSS variables whenever possible instead of props. So we keep using className/style props for styling as much as possible. It might be slightly faster as well. What we lose though is TypeScript typings, so it's hard to keep track of possible classNames.Kassandra
Hi, I've created a Code Sandbox demo using classNames instead of the usual function based pattern: codesandbox.io/s/styled-components-classname-6od0i?file=/src/…. I like this syntax because it's more respectful of the CSS, but it ends up being slightly slower, any idea why?Kassandra
I was getting an error 'Unknown word CSS: fake-element-placeholder-0', adding a semi colon at the end helped ${({ active }) => active && background: blue; };Karb
Looks really weird, comparing to elegance of classesLunatic
C
67

I didn't notice any && in your example, but for conditional rendering in styled-components you do the following:

// Props are component props that are passed using <StyledYourComponent prop1="A" prop2="B"> etc
const StyledYourComponent = styled(YourComponent)`
  background: ${props => props.active ? 'darkred' : 'limegreen'}
`

In the case above, background will be darkred when StyledYourComponent is rendered with active prop and limegreen if there is no active prop provided or it is falsy Styled-components generates classnames for you automatically :)

If you want to add multiple style properties you have to use css tag, which is imported from styled-components:

import styled, { css } from 'styled-components'
// Props are component props that are passed using <StyledYourComponent prop1="A" prop2="B"> etc
const StyledYourComponent = styled(YourComponent)`
  ${props => props.active && css`
     background: darkred; 
     border: 1px solid limegreen;`
  }
`

OR you may also use object to pass styled, but keep in mind that CSS properties should be camelCased:

import styled from 'styled-components'
// Props are component props that are passed using <StyledYourComponent prop1="A" prop2="B"> etc
const StyledYourComponent = styled(YourComponent)`
  ${props => props.active && ({
     background: 'darkred',
     border: '1px solid limegreen',
     borderRadius: '25px'
  })
`
Considerable answered 29/1, 2018 at 13:48 Comment(3)
{ css } - 👏🏽. I didn't know about that. Where is it in the docs?Deflocculate
@Deflocculate it's there: styled-components.com/docs/api#cssConsiderable
It seems like it's working without css tag if you just return a simple string with valid styles. Please correct me if I am wrong.Citrus
T
25

Here is an simple example with TypeScript:

import * as React from 'react';
import { FunctionComponent } from 'react';
import styled, { css } from 'styled-components';

interface IProps {
  isProcessing?: boolean;
  isDisabled?: boolean;
  onClick?: () => void;
}

const StyledButton = styled.button<IProps>`
  width: 10rem;
  height: 4rem;
  cursor: pointer;
  color: rgb(255, 255, 255);
  background-color: rgb(0, 0, 0);

  &:hover {
    background-color: rgba(0, 0, 0, 0.75);
  }

  ${({ disabled }) =>
    disabled &&
    css`
      opacity: 0.5;
      cursor: not-allowed;
    `}

  ${({ isProcessing }) =>
    isProcessing &&
    css`
      opacity: 0.5;
      cursor: progress;
    `}
`;

export const Button: FunctionComponent<IProps> = ({
  children,
  onClick,
  isProcessing,
}) => {
  return (
    <StyledButton
      type="button"
      onClick={onClick}
      disabled={isDisabled}
      isProcessing={isProcessing}
    >
      {!isProcessing ? children : <Spinner />}
    </StyledButton>
  );
};
<Button isProcessing={this.state.isProcessing} onClick={this.handleClick}>Save</Button>
Tintinnabulation answered 12/12, 2019 at 11:6 Comment(1)
this is the only way worked well for me, because it contains the Type and Css function, great job!Lambdoid
K
13

I haven't seen this syntax, which I feel is the cleanest when you need to make a complete block conditional:

const StyledButton = styled(button)`
    display: flex;
    background-color: white;

    ${props => !props.disabled} {
        &:hover {
            background-color: red;
        }

        &:active {
            background-color: blue;
        }
    }
`;

So there's no need to close/open ticks to get it working.

Kisner answered 12/11, 2020 at 15:42 Comment(6)
I like that syntax, but are you sure it works for non-boolean checks? (ex. props => props.foo === "bar") doesn't seem to work for me to check a specific valueDeflower
It works as I use the syntax quite often. You might want to put a console.log in there to check that "foo" is actually defined.Kisner
Could you please elaborate? I don't get why it works, is that specific to how Styled Components work?Kassandra
yes, I confirm that TS complains when I try to do something like: ${(props) => props.$isSelected} { ... Scylla
this syntax doesn't work, I past the same code to my project, but get some eerros.Inapprehensive
If it's in TypeScript, you might need to write it as ${props:Something => ... (or props:any)Kisner
P
6

If your state is defined in your class component like this:

class Card extends Component {
  state = {
    toggled: false
  };
  render(){
    return(
      <CardStyles toggled={this.state.toggled}>
        <small>I'm black text</small>
        <p>I will be rendered green</p>
      </CardStyles>
    )
  }
}

Define your styled-component using a ternary operator based on that state

const CardStyles = styled.div`
  p {
    color: ${props => (props.toggled ? "red" : "green")};
  }
`

it should render just the <p> tag here as green.

This is a very sass way of styling

Primogenitor answered 5/4, 2019 at 15:26 Comment(0)
K
0

It seems to be possible to use classNames as well, by applying them conditionally:

const BoxClassname = styled.div.attrs((props) => ({
  className: clsx(props.$primary && "primary")
}))`
  background: #000;
  height: 1px;
  width: 50px;
  &.primary {
    background: pink;
  }
`;

/*
// You could also use a second component instead of .attrs
export const BoxClassname = (props) => {
  return (
    <BoxClassnameWrapper
      className={clsx(props.$primary && "primary")}
      {...props}
    />
  );
};
*/

What I like in this syntax is that you don't mix JS and CSS too much.

Limitation is that it seems slower, see this demo code sandbox for a perf comparison. I don't really get why though :/ because logically.

I had this idea after reading Josh Comeau take on using CSS variables in Styled Components.

  • CSS variables let's you configure... variables (switching colors etc.)
  • Logically, classNames + CSS selectors let's you define conditions

After all, this logic exists in CSS already, className are already meant for handling conditional rendering. Styled Components helps keeping the styles cleanly isolated and handle advanced scenarios, but I don't like it meddling too much with the styles.

Kassandra answered 22/9, 2021 at 15:22 Comment(0)
R
0

This answer is for all the people who are using their official guide. According to their official docs we can do conditional rendering like this but in my case it didn't work


const Button = styled.button<{ $primary?: boolean; }>`
  /* Adapt the colors based on primary prop */
  background: ${props => props.$primary ? "#BF4F74" : "white"};
  color: ${props => props.$primary ? "white" : "#BF4F74"};

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

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

This is how I solved that


export const Icon = styled.img`
 ${({ password }) => password && `
    cursor: pointer;
  `}
`

Rennes answered 30/4, 2024 at 8:7 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.