Skip to main content

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 the styled.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:

  1. background: ${props => ...}: Inside our CSS block, we use an interpolation ${...}.
  2. props => (props.$primary ? ...): We pass an arrow function that receives the component's props. We then use a ternary operator to check if props.$primary is true.
  3. <Button $primary>: When we render the component, we pass the $primary prop just like any other React prop. This makes the props.$primary expression true 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: The styled(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.

Further Reading