Getting Sassy with styled-components

Progressively migrating your app from Sass to styled-components seems daunting, but with a couple of quick tips and the right tooling it’s a joy!

If you’re like me, then you’ve already discovered how great working with styled-components is. We can easily create reusable React components with encapsulated styles that can be adapted based on props. (If you’re new to styled-components, go check it out, I’ll wait.)

But what if, like me, you can’t just start from scratch every time something new comes out? What if your projects implement components based on a style guide built in Sass? How then can we progressively migrate our app to use styled-components?

The Basic Button

Let’s look at a common example, the Button component. Our CSS button includes a default style as well as some variants as show below.

Basic Button with Variants

We can start with the default button. In our stylesheet, we have a .btn class that defines some basic styles for a button as seen below. Converting this to a styled component is fairly trivial.

Sass Stylesheet for the Base Button

Setup

We start by importing the styled-components package.

Define the Component

In most cases it makes sense for our Button component to render as a <button> element. To do this, we use styled.button. (There may be times when you want a different tag, we’ll get to that.)

Move the Styles

We can simply copy over the base styles from our stylesheet and put them inside the template literal.

Basic Button as a Styled Component

That’s it. Now we’ve got our base Button component that can be used like any other React component.

<Button>Click Me</Button>

Button Variants

With our default Button component done we can move on to the other variations. Our stylesheet defines variant styles for things like colors (primary | danger), sizes (small | large), and types (inverted | link | disabled). This is where things get a bit more interesting and where we get to see the power of styled-components’ template literals.

Sass Stylesheet for Base Button and Variants

Interpolations

With styled-components we can pass an interpolated function to the template literal and it will receive all the props that were passed to the component. Using this we can adapt our styles based on these props.

Mapping Variants to Props

To get started, we need to map the different variants to props. Let’s use colors as an example. To get a primary or danger button using the stylesheet we add either the .btn--primary or .btn--danger class. For our styled component we can implement primary and danger props. Using the interpolated function, we adjust the styles based on these props. Here’s what the function looks like:

background-color: ${props =>
(props.primary && '$primary') || (props.danger && '$danger')
}

Following this pattern we can go ahead and add interpolated functions for the other variants. The result looks like this:

Styled Button Component with Sass Variables

Reusing Variables

You’ll notice that the styles we carried over still reference Sass variables. Right now our component won’t render as expected because the variables are not valid CSS values. We could replace each variable with its calculated value but then we lose the benefits of using variables in the first place. So how can we retain use of our Sass variables inside our new component?

Styled Themes

If we look at our Sass variables, we see they are just key:value pairs. By using these variables in our components we are essentially applying a global theme to our app.

Lucky for us styled-components supports it’s own version of theming via the <ThemeProvider> wrapper component. This component accepts a theme prop that is just an object with key:value pairs. By wrapping our app in a <ThemeProvider> component, our <Button> component will have access to the theme as another prop.

So how do we transform our Sass variables into a theme object? We can use the sass-extract library combined with the sass-extract-js plugin to help us out. We pass in the path to our Sass file with global variables and get back a JS object ready to use as a theme. By using our main Sass file to generate the theme we ensure that our CSS components and Styled components stay in sync even if we make changes to the variables.

Example

First we need to make sure to install the required packages:

npm install -S sass-extract node-sass sass-extract-js

Then we can set up our main app file. We’ll import our dependencies, generate our theme object, and define our <App> component.

Implementing ThemeProvider with Theme Generated by sass-styled-theme

With our theme in place, we can update our Button component to reference it instead of our Sass variables.

Complete Styled Button Component

I created the pen below so we can see our Button component in action alongside the original CSS button. You can play around and see how it all comes together.

CSS Button and Styled Button Side-By-Side

When a Button isn’t a <button>

So we’ve got our Styled component all set up and our variants in place and we’re all set to start using it. We start coding up a layout and realize that the layout calls for a text link. No problem. Our Button component has a link prop that will render it as text.

But suppose you decide instead of rendering a <button> tag what you really want is an <a>. With styled-components this is trivial to implement and it doesn’t involve copying/pasting any of our styles.

withComponent()

We can easily create a new component based on our Button using the withComponent method. This has the benefit of retaining all the styles that we have defined for our Button but it will simply render an <a> tag. Read more about this.

Create a Component that Renders With a Different Tag

Sass Extraction

The two packages that make all this possible are sass-extract and sass-extract-js. Let’s take a closer look at our initial Sass variables and the extracted theme object. Consider our variables.scss file below:

Sass Variables

By passing this variables file into sass-extract’s renderSync() function and specifying the sass-extract-js plugin, we get the following theme object, all set to pass into our <ThemeProvider>:

Generated Theme Object

Sass + Styled = 💖

There’s no doubt that migrating an app to use styled-components is a big undertaking. But with its support for themes and great tools like sass-extract and sass-extract-js we don’t need to transition all at once. We can start small and add new Styled components as we go that will live alongside our CSS components. All without fear of visual inconsistencies creeping in.