https://www.joshwcomeau.com/css/styled-components/ JoshWComeau * Latest * Posts * Snippets * Goodies CSS The styled-components happy path My personal suite of "best practices" For a few years now, my #1 favourite tool for managing CSS in React apps has been styled-components. It's a wonderful tool. In many ways, it's changed how I think about CSS architecture, and has helped me keep my codebase clean and modular, just like React! It shares something else in common with React: developers often dislike the idea at first . "Every style is a component" can be a hard pill to swallow, just like "your views are now written in an XML /JS hybrid". Maybe as a result, I've discovered that a lot of developers never really fully embrace styled-components. They pop it into their project without updating their mental models around styling. One foot in, and one foot out. As a result, they miss out on some of the best parts of the tool! If you work with styled-components, or a similar tool like Emotion, my hope is that this article will help you get the most out of it. I've distilled years of experimentation and practice into a few practical tips and techniques. If you apply these ideas, I genuinely believe you'll be a happier CSS developer Intended audience This article is primarily written for React developers of all experience levels who are already using styled-components, or another CSS-in-JS solution like Emotion. This article is not meant to serve as an introduction to styled-components, nor is it meant to compare or contrast it with other tools. Link to this heading CSS Variables Let's start with a fun little tip. Say we have a Backdrop component, and it takes props for opacity and color: jsx How do you apply those properties to the Wrapper? One way would be to use an interpolation function: jsx This works alright, but it's fairly high-friction. It also means that whenever these values change, styled-components will need to re-generate the class and re-inject it into the document's , which can be a performance liability in certain cases (eg. doing JS animations). Here's another way to solve the problem, using CSS variables: jsx CSS variables are the gift that keeps on giving. If you're not sure what's going on here, my CSS Variables in React tutorial will help you make sense of it (plus you'll learn a few other neat tricks!). We can also use CSS variables to specify default values: jsx If we call without specifying an opacity or color, we'll default to 75% opaque, and our color theme's dark gray color. It just feels nice. It isn't game-changing, but it brings me a little bit of joy. But that's just the beginning. Let's look at something meatier. Link to this heading Single source of styles If you only take one thing away from this blog post, make it this tip. This is the mother lode. On this blog, I have a TextLink component. It looks something like this: jsx This is the component I use for links within content, like this blog post. Here's an example (Note: This link is meant to be a visual demo, you aren't meant to click it). On my blog, I have an Aside component which is used to provide bonus little bits of information: This is an example "Aside", with an included link. The words "an included link" in that Aside are rendered with a TextLink, the exact same component! Like an early-stage-startup employee, TextLink has a lot of hats to wear. This is what I'd call a "contextual style". The same component changes appearances depending on its context. When you pop a TextLink into an Aside, some styles are added/replaced. How would you solve for this situation? I often see stuff like this: jsx In my opinion, this is a five-alarm-fire situation. We've made it so much harder to reason about the styles in our application! How would you ever find out that TextLink could be given these styles? You can't do a project-wide search for TextLink. You'd have to grep for a, and good luck with that. If we don't know that Aside applies these styles, we'll never be able to predict it. So OK, what's the right approach? Maybe you've thought about specifying these styles using TextLink instead of a: jsx styled-components allows us to "embed" one component in another like this. When the component is rendered, it pops in the appropriate selector, a class that matches the TextLink styled-component. This is definitely better, but I'm not a happy camper yet. We haven't solved the biggest problem, we've just made it slightly easier to work around. Let's take a step back and talk about encapsulation. The thing that made me love React is that it gives you a way to pack logic (state, effects) and UI (JSX) into a reusable box. A lot of folks focus on the "reusable" aspect, but in my opinion, the cooler thing is that it's a box. A React component sets a strict boundary along its perimeter. When you write some JSX in a component, you can trust that the HTML will only be modified from within that component; you don't have to worry about some other component on the other side of the app "reaching in" and tampering with the HTML. Take another look at that TextLink solution. The Aside is reaching in and meddling with TextLink's styles! If any component can overwrite any other component's styles, we don't really have encapsulation at all. Imagine how much nicer it would be if you knew, with complete confidence, that all of the styles for a given element were defined right there, in the styled-component itself? Well, it turns out, we can do that. Here's how: jsx jsx If you're not familiar with the & character, it's a placeholder for the generated class name. When styled-components creates a .TextLink-abc123 class for this component, it'll also replace any & characters with that selector. Here's the CSS it generates: css With this little trick, we've inverted the control. We're saying "Here are my base TextLink styles, and here are the TextLink styles when I'm wrapped in AsideWrapper". All in 1 place. Our mighty TextLink is in charge of its own destiny once more. We have a single source of styles. Doing it this way is seriously so much nicer. Give it a shot the next time you run into this situation. The right tool for the job When we have two generic components like Aside and TextLink, this form of "inverted control" makes things so nice and predictable. But does it make sense in all contexts? Let's imagine we have a component, HalloweenSale.js. It's a marketing page that uses our standard components, but adds a spooky font and orange color theme. Should we update all of our reusable components to include this variant? Goodness no. Show more Inherited properties In some ways, there's a bit of a round-hole-square-peg situation happening: component boundaries will never be totally airtight, because certain CSS properties are inheritable. In my opinion, though, this isn't a big deal. Show more Link to this heading Isolated CSS Alright, I have one more big idea to share. Let's say that we want that Aside component to have some space around it, so that it isn't stuck right up against its sibling paragraphs and headings. Here's one way to do that: jsx This'll solve our problem, but it also feels a bit pre-emptive to me. We've locked ourselves in; what happens when we decide to use this component in another situation, one with different spacing requirements? Cute illustration of a personified gluestick, tipping its hat Using margin in this way is antithetical to the idea of reusable, generic components. Heydon Pickering has a great quote about this: "Margin is like putting glue on something before you've decided what to stick it to, or if it should be stuck to anything." There's also the fact that margin is weird. It collapses in surprising and counterintuitive ways, ways that can break encapsulation; if we put our