Conditional styling using Styled Components

Viewed 2089

I've been trying for hours to figure this out, and I'm sure the answer is simple. When an Item is clicked, it will ultimately create a filter for my todo list. What I'm struggling to do is apply conditional styling, so that the font color of the 'active' item is blue.

I have included status as a prop, but I can't seem to use it as well as props inside the styled components.

From my App.js:

import { useState } from 'react'

function App() {
  const [ status, setStatus ] = useState("all")

  return (
    <Filter status={status} setStatus={setStatus}/>
  )
}

Filter.js

const Container = styled.div`
    display: flex;
    justify-content: center;
`

const Item = styled.p`
   color: red;

   // I'm trying to do something like this..

   ${(status, props) => {
        if (status === props.id) {
            return `
                color: blue;
            `
        }
    }}
   
`

export const Filter = ({ setStatus, status }) => {
    
    const statusHandler = (e) => {
        if (e.target.id) {
            setStatus(e.target.id)        
        }
    }
    
    return (
        <div>
          <Container onClick={statusHandler}>
            <Item id="all">All</Item>
            <Item id="active">Active</Item>
            <Item id="completed">Completed</Item>
          </Container>
        </div>
    )
}
3 Answers

I think the easiest way to perform this task is to add a class at the element you want to change the color.

So you're item will be

const Item = styled.p`
   color: black;
   &.selected {
    color: red
   }

and you're Component will be:

<>
    <Item id="all" className={status === "all" ? "selected" : null}>All</Item>
    <Item id="active" className={status === "active" ? "selected" : null}>Active</Item>
    <Item id="completed" className={status === "completed" ? "selected" : null}>Completed</Item>
</>

To avoid to write repeating code you can loop the values. And here we go:

const items = [
    {
        id: "all",
        value: "All"
    },
    {
        id: "active",
        value: "Active"
    },
    {
        id: "completed",
        value: "Completed"
    },
]

items.map(({id, value}) => (
    <Item key={id} id={id} className={status === id ? "selected" : null}>{value}</Item>
))

There's no need to put logic in Styled Component if you can keep in React

Pass both the status and id props to the Item component and do the comparison in the props callback.

const Item = styled.p`
  color: red;

  ${({ id, status }) => status === id && 'color: blue; '}
`;

Usage:

<Item id="all" status={status}>All</Item>

Demo

Edit conditional-styling-using-styled-components

You could make this simpler though with a single prop and do the conditional test in the component.

const Item = styled.p`
  color: red;

  ${({ isStatusMatch }) => isStatusMatch && "color: blue;"}
`;

...

<Item isStatusMatch={status === 'all'}>All</Item>

Full demo code:

const Item = styled.p`
  color: red;
  ${({ id, status }) => status === id && "color: blue;"}
`;

const Item2 = styled.p.attrs(({ id, status }) => ({
  className: id === status && "statusMatch"
}))`
  color: red;
  &.statusMatch {
    color: blue;
  }
`;

const Item3 = styled.p`
  color: red;
  ${({ statusMatch }) => statusMatch && "color: blue;"}
`;

function App() {
  return (
    <div className="App">
      <Item id="test" status="test">
        test
      </Item>
      <Item id="nottest" status="test">
        not test
      </Item>

      <Item2 id="test" status="test">
        test
      </Item2>
      <Item2 id="nottest" status="test">
        not test
      </Item2>

      <Item3 statusMatch={"test" === "test"}>test</Item3>
      <Item3 statusMatch={"nottest" === "test"}>not test</Item3>
    </div>
  );
}

enter image description here

You can try something like:

// YOUR STYLES
const activeStyles = (props) => ({
  color: props.status === props.id ? 'blue' : null
});

// YOUR FILTER COMPONENT
    return (
        <div>
          <Container onClick={statusHandler}>
            <Item id="all" styles={{...activeStyles({status, id:'all'})}}>All</Item>
            <Item id="active" styles={{...activeStyles({status, id:'active'})}}>Active</Item>
            <Item id="completed" styles={{...activeStyles({status, id:'completed'})}}>Completed</Item>
          </Container>
        </div>
    )

Related