The state of CSS, CSS in JS & how styled-components is solving the problems we’ve had for decades

Oct 31, 2018 · 13 min read

Someone is going to unify these three different syntaxes and write a language that just addresses the web platform directly, and it’s going to be insanely popular.

— Jeremy Askenas, creator of coffeescript, 2015

The journey to styled-components has been long, but with it’s simplicity, tooling and level of adoption, it’s the future of CSS in JS, and IMHO, the emerging standard that is about to stay. I can mention the disadvantages of CSS in JS, but most of them will not be valid if we’re using styled-components. The topic is probably one of the fastest evolving in web development, so I hope you’ll see:

  • advantages of CSS in JS
  • drawbacks, and how CSS Modules tried to solve this
  • how styled-components addressed the above two and delivered. The disadvantages of inline styling, does not apply.

That’s it. No extra tooling, setup necessary to use styled-components. No need to set up node-sass, or LESS. To find out why, do read on.

Before I take you on a journey, I wanted to list down the advantages of CSS in JS. I couldn’t concisely do it. Why? Because CSS in JS doesn’t have the same disadvantages as it used to have, even a few months ago.


Premise

My journey in style things on the web looks somewhat like so:

The world before CSS:

https://css-tricks.com/look-back-history-css/

Styling was definitely on the cards. And it was tedious.

We needed a language that would describe the output of the page. The above example was just not feasible over large markups. I rather let CSS Tricks get the credit for this amazing article on the early years.

Early CSS Competitors:

Proposals for styling elements

And then, there was CSS. Cascading style sheets, one thing is, it didn’t really cascade in it’s structure, but rather its declarations.


I’m going to assume that you know the drawbacks of vanilla CSS. While I paint you the lovely picture of what styled-components has achieved, let’s put a marker down on what’s going to be covered.

Assumptions

  • CSS Modules are one of the CSS in JS options (I have explained why, IMHO, in part II below)
  • Framework related examples below are mostly React-centric, but they will apply to other frameworks (e.g. class names in Vue).
  • As more features of future CSS specifications are implemented by browsers, the need to code style sheets another syntax become unnecessary by the day. i.e. CSS & the browsers are catching up fast. Just look at the features that would be supported in the future below. It’s stuff you would need a pre-processor for.
The upcoming specifications of CSS

I will focus on three CSS in JS techniques.

  • Inline styles
  • CSS Modules
  • Styled-components

Problem statement (a.k.a disadvantages of externally loaded, traditional CSS)

If ever we analyze how we got here, it seems like every step of the way, the limitations of CSS was glaring. We needed variables, nesting, the ability of breaking down out styles into smaller files, calculations and dynamism — things we normally jump to SASS/LESS for.

Tool-chain & Features

I had my fair share of node-sass errors:

Image result for node sass errors

One of the reasons CSS in JS is such a nice option is that we don’t need an extra build step for CSS. The problem with SASS/LESS is that variables, mixins and other features are transpiled into CSS during the build phase. And you needed to build your CSS. That’s one more library to depend on, one more build step (at least), and the end product is CSS.

Tediously manipulating visual state

Direct style manipulation using JS

I will be skipping the dark time in our past when we were using JS to manipulate style properties directly. I understand that it’s an important part of the evolution, but I’d rather focus on class names for two reasons:

  • in the end, CSS in JS pretty much does this anyway — manipulate the style properties.
  • Class names are still used in React, so it would be to focus on why CSS in JS’s principle of doing away with class names is a great step for mankind.

Anyway, I’ll just leave an example of what I’m talking about before moving on:

CSS manipulation by JS using inline styles

By the way, the code above is wrong :) You need to implicitly set the unit like so:

Taming class names

For an element in the DOM to change visually, class names were the only way to go. Have a look below at the madness of class names.

These aren’t the best examples, but I believe it conveys the point

Have a look at the pen below to see a couple of ways class names can be worked with in React.

Class names are redundant. They are an extra link between external style sheets and result in the parent components dictating through numerous class names, how the child should look & feel. One might soon face the issue of why a style isn’t being applied and it would be because of a class name of a great-grandparent that’s forcing styles down or even one of it’s own class names that are conflicting. And no amount of block__element–modifier hacks will ease the pain. I sometime spend time thinking of the proper class names for BEM. Extra cognitive load.

Check out those classes! What if there was a typo there!

Now, what about just plain ol’ CSS. Let’s dive in briefly.

That’s a deep specification, and it’s relying on a specific combination of markup to style that element way down the DOM. It’s also something that you’d spend more time debugging in your browser. Once more, there’s unnecessary cognitive load when coding/debugging.

The above code would beg for a developer to think “There’s gotta be a better way”.

Finally, a short list:

  • Class name selection became a tedious task
  • Markup became bloated with all those long class names
  • You needed to explicitly extend every UI component whenever you wanted to reuse, resulting in resetting CSS rules. Take .btn in Twitter Bootstrap. .btn .btn-link is used to denote a button that looks like a link. What .btn-link does is remove some of the styling already applied to the .btn.
  • Markup becomes unnecessarily semantic. You will suddenly start duplicating your DOM structure in CSS.

Redundancy in the component age


Disadvantages of CSS in JS (Inline styling)

If you’re considering CSS in JS, there’s something that needs to be stated. Javascript is expensive:

Byte-for-byte, JavaScript is more expensive for the browser to process than the equivalently sized image or Web Font — Tom Dale

The Cost Of JavaScript — Addy Osmani — Medium

There are many CSS in JS libraries out there. Take the following example:

Notice the camelCase. This is prevalent in CSS in JS, including React’s built-in way for styling elements:

Ermmm. CamelCase? No, thanks, mainly because:

  • Debugging: The browser still understands this as CSS, which means the above styles are displayed as regular CSS in the browser. You will be coding in camelCase and debugging kebab-cased css properties in the browser.
  • Code adoption: Copy & pasting of code from good ol’ CSS requires refactoring the case of the CSS property. Unnecessary.
  • More variables: There is an extra ‘link’ from the style prop (style={divStyle}) to the style object - in large code bases, personally I find myself needing to scroll around for the corresponding style declaration.
  • Inconsistent with CSS Specification: Custom CSS properties use hyphens, and a PR last year allowed for that to be used in React via inline styles. But let’s have a look at the code if custom properties are used:

I know the above reasons center predominantly around the case of characters, and this PR shows that many prominent React devs are rooting for camelCase CSS. Dan Abramov’s suggestion of using the React context API was also downvoted heavily. To end, here’s also a crucial discussion to not force camel-case via inline styling in React.

Huge credit goes to CSS in JS 101 for doing detailed assessment.

To list down more disadvantages of inline styling, I’m going to rip it out of the above link.

  • Code duplication in case of server-side rendering.
  • Additional costs in JS payload. Remember that styles which are embedded in JS are not for free. It is not only about download time, it is also about parsing and compiling. Javascript is expensive, as mentioned above.
  • No media queries (@media) — you’re expected to use React to render different styles based on the view-port size, taking a huge responsibility of CSS away from the browser and into your code.
  • No CSS animations (@keyframes)
  • No pseudo classes (:hover)
  • No web fonts (@font)
  • No autoprefixer (well there is inline-style-prefixer)

CSS in JS — Part two: CSS Modules

CSS Modules was co-created by Glen Maddern, who went on to create styled-components with Max Stoiber. I think it’s impossible to head into what CSS in JS is now without touching on this technology.

Is CSS Modules CSS in JS? It’s a gray area. I feel that since we’re dictating what styles should be loaded and mapped to a certain element in JavaScript, it can’t be not CSS in JS. A huge decisive function of the CSS interpreter is heavy-handedly implemented in JS. Importing CSS as modules solves the issue with class names.

The following is an excerpt from a great article by Object Partners.

CSS Modules is an implementation of CSS in JS, so if you leave here with nothing else, consider integrating CSS Modules support into your application. It’ll generate a unique hash based on a user supplied class name.

CSS in JS: Benefits, Drawbacks, and Tooling | Object Partners

In the briefest possible introduction, CSS modules do the following:

A simple React example:

What’s rendered:

The random classes mean that you (almost) never need to worry about cross-component style’s clashing. However, as we’re importing CSS files, developers could resort to use SASS or LESS as they miss the basic features such as mixins & rule nesting, so even in the world of CSS Modules, that extra build step / tool-chain is necessary.

CSS in JS — Part 2b: The <style/> tag! (Honorary mention)

The <style/> tag? How? Well, I mentioned earlier that @media queries don’t work using inline styles. However, this following nifty method allows you to pretty much write all acceptable CSS right into your component :) Credit.

Which renders to:

And @media queries work! Cool eh? It’s too cool not to have a go. Play around with the following example, which includes uniquely generated class names. I learnt a little more about the simplicity of React!

This is an escape hatch I wouldn’t push this approach very far. I wouldn’t try to rally a team around it. It is something I keep in my pocket for times I get sucked into the CSS-in-JS turf wars. It’ll help you kick the can down the road a little.

The style tag and React — learnreact


CSS in JS — Part three: Styled-components

Visual primitives for the component age. [styled.components.com]

Let’s look at the button again. In the world of styled-components, you’d end up with:

Syntax highlighting provided by Vim plugin for styled-components
// The markup for the above button
<Button primary>Happy styled button</Button>

In a nutshell, you get a lot of sassy features like nesting, and the architecture of style-components means your props basically allow you to write mixins in pure JavaScript.

Read: Getting Sassy with styled-components — 💅 styled-components — Medium

Performance

Rather not go into detail here, but you can get a report from April, 2018. Note, styled-components has only gotten faster since then!

styled-components v4 mounts faster by ~25% for both deep and wide component trees, and updates dynamic styles faster by ~7%

Announcing styled-components v4: Better, Faster, Stronger 💅

There are many libraries out there that allow the use of CSS in JS. You can see a good list in this article, when I got the image below from.

speed-chart
Credit

As you can see, styled-components v4 is blazing fast™️. We’re within a standard deviation of all the fastest libraries out there, both in terms of mounting as well as updating speed, which means performance is officially no longer an issue! 🎉

Announcing styled-components v4: Better, Faster, Stronger 💅

Advantages (a.k.a why styled-components should be taken seriously)

The concept is being ported over other CSS in JS libraries & frameworks

I believe the concept of styled-components is the ultimate CSS in JS option. Writing pure CSS in React without any camelCasing means you debug your styles in the same language as the browser. Also, using props instead of classnames makes testing the component awesome and easy with jest and/or enzyme.

Note, styled-components was created by Max Stoiber and Glen Maddern, the latter who wrote “CSS Modules: Welcome to the future” in 2015.

Because the concept is so simple and neat, there are official Vue & Vanilla HTML implementations of it. I am hoping to see styled-components being implemented with shadow DOM capability for custom elements.

The emotion CSS in JS library predates styled-components, but if you look at their docs, it starts off with:

styled is a way to create React or Preact components that have styles attached to them. It’s available from react-emotion and preact-emotion. styled was heavily inspired by styled-components and glamorous

— emotion-docs

You can see that the official Vim plugin for styled-components supports other CSS in JS libraries because said libraries support the styled syntax. This is a pattern you see with many tools that are built for styled-components.

Vim bundle for styled-components, diet-cola, emotion and experimental glamor/styled content in javascript files.

— vim-styled-components

If anything, I hope you take away one thing from this. Styled-components is actively developed and other libraries are also supporting the concept of it.

React-native & Web universal components

styled-primitives

Gone are the days you need to write two stylesheets for react-native & web. Whilst there are options out there for one shared code base between native & web, styled-components implements it at a primitive level.

Creating truly universal React component systems — 💅 styled-components — Medium.

The future is bright with primitives. Note, primitives still need a lot of work when I used them a few months ago. But I have managed to use most Native primitives without any glaring issues. I would do ̶s̶o̶m̶e̶ a lot of due diligence if you’re planning this for production. In fact, I might take a deeper look into them in a future article.

Ecosystem

I knew styled-components was going to be big! But checkout out the supporting tools! It’s not even a list of plugins — it’s, as mentioned, an entire ecosystem.

You can find anything from a set of Sass/Compass-style mixins/helpers, to grid-systems & boilerplates.


Tooling

Here’s where styled-components pulls another win.

There is everything from a babel-plugin that transpiles styled-components to be more performative in production, server-side rendering, jest integration & automated snapshot testing, debugging, syntax highlighting, linting, type systems, dead code elimination and also a superb theming layer (that’s only getting much simpler by the day!).

Autocompletion for CSS properties in VSCode
Autocompletion for CSS values in VSCode
Code with confidence
Use existing stylelint rules and get error checking

Read more about the tooling here.


Adoption (will keep updating this section with more links)

Styled-components is so easy to setup — you don’t need any extra build settings (to start development). Checkout these amazing libraries:


To end :)

I hope you saw the journey to styled-components & the benefits of coding styles using this amazing library in the component age. Do leave a comment below if you have any queries / errata to point out. This is my first Medium article and I hope to have more.


Vik Ramanujam

Written by

Technical Lead, Front-end Ninja @ BAE Systems Applied Intelligence

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade