Introduction to CSS-in-JS (Part 2): Dynamic `styled-components` #30
📖 Introduction
In the previous article, we introduced the concept of CSS-in-JS and created our first static components with styled-components
. The real power of this pattern, however, lies in its ability to create dynamic styles that adapt based on the props passed to the component.
This article dives into the most exciting feature of styled-components
: using props to dynamically change styles, create variations of a component, and build a truly reusable and configurable component system.
📚 Prerequisites
Before we begin, please ensure you have a solid grasp of the following concepts:
styled-components
Basics: You should know how to create a simple styled component using thestyled.div\
...`` syntax.- React Props: You must be comfortable passing props from a parent component to a child component.
- JavaScript Template Literals & Functions: Understanding how to use functions within template literals (
${() => ...}
).
🎯 Article Outline: What You'll Master
In this article, you will learn:
- ✅ Adapting Based on Props: The core technique for making your styled components dynamic.
- ✅ Using Functions in Styles: How to pass a function to a template literal to access your component's props.
- ✅ Creating Component Variants: Building different versions of a component (e.g., primary and secondary buttons) using props.
- ✅ Extending Styles: How to create a new styled component that inherits and overrides the styles of another.
🧠 Section 1: The Core Concept: Adapting Based on Props
Because styled-components
are real React components, they can receive props just like any other component. The magic of the library is that you can access these props directly inside your CSS block to change the styles.
This is done by passing an arrow function inside an interpolation (${...}
) within your template literal. This function will automatically receive the component's props as its first argument.
const MyStyledComponent = styled.div`
/* The 'props' object is automatically passed to this function */
background-color: ${props => (props.primary ? 'blue' : 'white')};
`;
In this example, the background-color
will be blue
if the component receives a primary
prop that is true
, and white
otherwise.
💻 Section 2: Building a Dynamic Button
Let's build a Button
component that changes its appearance based on a $primary
prop.
Note on Transient Props: styled-components
recommends prefixing props that are only used for styling with a dollar sign ($
). This prevents them from being passed down to the underlying DOM element, keeping your HTML clean.
// code-block-1.jsx
import React from 'react';
import styled from 'styled-components';
const Button = styled.button`
/* Adapt the colors based on a $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;
cursor: pointer;
`;
function App() {
return (
<div>
<Button>Normal Button</Button>
<Button $primary>Primary Button</Button>
</div>
);
}
export default App;
Code Breakdown:
background: ${props => ...}
: Inside our CSS block, we use an interpolation${...}
.props => (props.$primary ? ...)
: We pass an arrow function that receives the component'sprops
. We then use a ternary operator to check ifprops.$primary
is true.<Button $primary>
: When we render the component, we pass the$primary
prop just like any other React prop. This makes theprops.$primary
expressiontrue
for that instance, and the styles adapt accordingly.
This creates a highly reusable Button
component whose variations are controlled declaratively through props.
🛠️ Section 3: Extending Styles
Sometimes you want to create a new component that is just a slight variation of another. While you could use props for this, styled-components
offers a cleaner way if the variation is not meant to be dynamic: extending styles.
You can create a new styled component based on another by passing the original component into the styled()
constructor.
Let's create a TomatoButton
that inherits all the styles from our Button
but has a different color scheme.
// code-block-2.jsx
import React from 'react';
import styled from 'styled-components';
const Button = styled.button`
color: #BF4F74;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid #BF4F74;
border-radius: 3px;
`;
// Create a new component that extends the Button
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
function App() {
return (
<div>
<Button>Normal Button</Button>
<TomatoButton>Tomato Button</TomatoButton>
</div>
);
}
export default App;
How it Works:
The TomatoButton
inherits all the CSS from Button
(padding, margin, etc.). The new styles we defined inside TomatoButton
(color
and border-color
) will override the styles inherited from the original Button
. This is a powerful pattern for creating themed component libraries.
✨ Section 4: Best Practices
- Use Transient Props (
$
): For props that are solely for styling and shouldn't be passed to the DOM, prefix them with a dollar sign (e.g.,$primary
,$isActive
). - Keep Logic Simple: For complex conditional logic, consider creating a helper function outside the style block to determine the style value. This keeps your CSS readable.
- Embrace Props for Variants: Creating component variants (like primary/secondary buttons or small/large inputs) is the primary use case for dynamic props.
- Use
extend
for Theming: Thestyled(Component)
pattern is excellent for creating different themes or one-off variations of a base component.
💡 Conclusion & Key Takeaways
You've now learned how to breathe life into your styled components, making them adapt to the data and state of your application. This ability to seamlessly blend JavaScript logic with CSS is the core reason why CSS-in-JS has become so popular.
Let's summarize the key takeaways:
- Styles Can Read Props: You can use an arrow function
(props => ...)
inside a${...}
interpolation to access your component's props. - Dynamic Styling is Declarative: You change a component's style by changing the props you pass to it, which fits perfectly with the React paradigm.
- Use
$
for Transient Props: Prefix styling-only props with a$
to prevent them from being passed to the underlying DOM element. - Extend Styles for Variations: Use
styled(ExistingComponent)
to create a new component that inherits and overrides styles from another.
Challenge Yourself:
Create a PasswordInput
styled component that extends a base Input
component. The PasswordInput
should have all the styles of the Input
, but if it receives an $error
prop, it should change its border-color
to red.
➡️ Next Steps
You now have a powerful set of tools for styling React applications, from plain CSS to the dynamic capabilities of styled-components
. In the next article, we'll take a brief look at another popular styling solution: "Advanced Styling: Tailwind CSS with React (Part 1)", which uses a utility-first approach.
Thank you for your dedication. Stay curious, and happy coding!
glossary
- Dynamic Styling: The ability to change a component's CSS properties based on its props or state.
- Interpolation: In the context of template literals, this refers to embedding a JavaScript expression inside a
${...}
placeholder. - Transient Props: A convention in
styled-components
where props intended only for styling are prefixed with a$
to prevent them from being passed to the DOM.