conditional rendering in styled components
ReactjsStyled ComponentsReactjs Problem Overview
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>
)
}}
Reactjs Solutions
Solution 1 - Reactjs
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;
`}
`;
Solution 2 - Reactjs
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:
I didn't notice any && in your example, but for conditional rendering in styled-components you do the following:
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'
})
`
Solution 3 - Reactjs
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>
Solution 4 - Reactjs
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.
Solution 5 - Reactjs
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
Solution 6 - Reactjs
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.