5 minutes guide to styled-components for modular components

Before we deep dive into styled-components, let's take a few moment to understand what is the tradeoff for using vanilla CSS. Otherwise jump straight to the styled-components portion.

-- Smashing Magazine

CSS

CSS is easy. CSS is difficult. You can write CSS as a external style sheet or as an internal style. And (I hope you seldom do this), as an inline style. Over the years, there are many improvements make to CSS. In modern browser, you could now declare CSS variables by doing the following:

root {
  --main-bg-color: blue;
}

.main {
  color: white;
  background-color: var(--main-bg-color);
}

However, if you work with CSS, most likely, you will experience how quickly it become unwieldy and messy if there is no proper structure. This is true for any kind of code you write. And let's face it, it takes a lot of code to write in vanilla CSS.

In order to solve this issue, CSS pre-processors and CSS naming-convention are created.

There are many benefits of using CSS pre-processors.

  1. Break down CSS into their respective file components
  2. Write reusable mixins and functions
  3. Write nested rules
  4. In-built compression of output CSS file via command-line
  5. Easily create variable
  6. Do complex math

For example, if you look at the source code of Bootstrap, you will see how they create breakpoints mixin.

@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {
  $min: breakpoint-min($name, $breakpoints);
  $max: breakpoint-max($name, $breakpoints);

  @if $min != null and $max != null {
    @media (min-width: $min) and (max-width: $max) {
      @content;
    }
  } @else if $max == null {
    @include media-breakpoint-up($name, $breakpoints) {
      @content;
    }
  } @else if $min == null {
    @include media-breakpoint-down($name, $breakpoints) {
      @content;
    } 
  }
}

Another way that we could manage CSS is to use the BEM convention. It is a CSS naming convention that breaks down to ${Block}__${Element} or ${Block}-${Modififer}.

For example, to fit into the BEM convention, a variation of a form may have a class like this form__input or form--simple.

If you are curious about how it is being used on websites, check out this github repo with a list of website using the BEM convention.

However, there is one issue not completely solved either by BEM or SASS. Your CSS still lives in the global scope. This means that if you are working on a huge code base, with unstructure CSS, there might be namespace collision and the CSS specificity rule will determine which CSS is being used.

Styled components

The rise of React and many javascript framework have promoted the components based architecture in building user interface. This has also leads to a choice of CSS-in-JS libraries to choose from. A few of the more popular used ones are JSS, emotion and styled-components. And we have also seen the rise of css-modules.

Using CSS libraries create modular and reusable CSS for you as a developer will have to declare the explicit dependencies and there is no more global scope for CSS.

Personally, I prefer to use styled-components for the fact that it's syntax looks almost similar to vanilla CSS. Also, it is a very well-maintained and widely used library. This means it is not going away anytime soon.

The learning curve of styled-components is not steep and I recommend reading the documentation to get a firm understanding of how it works. For people who are trying to dip their toes into styled-components, here are 4 ways I create modular and reusable code with it.

Setting up theme provider

I'm a fan of learning from the giants. Reading and understanding the code of open-source projects have made me a better developer.

Sass brings about the benefit of having supercharged variable function to CSS. Using CSS-in-JS brings about the same benefits.

This could be done by setting up a <ThemeProvider> wrapper component provided by styled-components.

-- React documentation

Another great library to inspire good theming practices is Material-UI default theme. They store typography, palette, breakpoints, zIndex and etc in their default theme to allow consistent design and ease of overriding the theme.

theme provider react button UI

Creating styled component

Once you have the initial setup done, you are ready to write your component. When building component, it is good practice from a bottom up approach by starting from the most basic building block. A good resource to look at composable UI components is Material-UI source code. Each UI component is built like a lego block, to be able to implemented easily.

In fact, for most of my projects, like this blog, I will use Material-UI and styled-components. Any part of the UI should be able to be moved easily to another project.

In the code snippet below, you will see how easy it is to access the theme props declared in the ThemeProvider.

styled-components button UI

Now that you've seen the code, you will notice the usage of the backticks: `. This is the Javascript template string and it is how styled-components styled your component. Well, that's a mouthful. Just like any regular template string, you could declare variables through ${} and even include functions into your styling. We will look into that next.

Extending existing styled component

In Sass, it is simple to extends and includes styles into another class through @extend and @include. Styled-components offers similar functionality. Since is component based rather than CSS class based, we can easily inherit the styling of another by wraping the component in the styled() constructor.

Look at the example below, both the LinkButton and ContainedButton will include the style of Button.

styled-components button mixin UI

Abstract reusable mixins

So far, we have gone through the basic functionality of styled-components. You might be asking, what about the mixins? Extending existing styled-components might create fragile components if we are not careful.

As a rule of thumb, extends are the best option when you’re expressing a relationship between semantic classes (or other semantic selectors). Because an element with class .error--serious is an error, it makes sense for it to extend .error. But for non-semantic collections of styles, writing a mixin can avoid cascade headaches and make it easier to configure down the line.

-- Sass documentation

Using the BEM naming convention, you could easily see that UI classes can be universally broken down into 3 parts: block, element and modifier. With this in mind, mixins could generally be applied to modifier. The benefit of using styled-components is that you could write conditionals as a ternary operator or or multiple ifs or even as a switch-case.

The conditionals could be written inside the component or as a mixin to be injected into the component. Again, remember that styled-components make use of template-literals, so it will evaluate the javascript code.

Conclusion

Here are my 4 ways on creating and maintaining modular components for my react project. What are some of the techniques you used to wield the monstrous CSS? Share your tips below.

Join the geek movement

Get updated with our latest posts