DEV Community

Cover image for CSS Modules vs CSS-in-JS. Who wins?
Sergey
Sergey

Posted on

CSS Modules vs CSS-in-JS. Who wins?

Introduction

In modern React application development, there are many approaches to organizing application styles. One of the popular ways of such an organization is the CSS-in-JS approach (in the article we will use styled-components as the most popular solution) and CSS Modules. In this article, we will try to answer the question: which is better CSS-in-JS or CSS Modules?

So let's get back to basics. When a web page was primarily set for storing textual documentation and didn't include user interactions, properties were introduced to style the content. Over time, the web became more and more popular, sites got bigger, and it became necessary to reuse styles. For these purposes, CSS was invented. Cascading Style Sheets. Cascading plays a very important role in this name. We write styles that lay like a waterfall over the hollows of our document, filling it with colors and highlighting important elements.

Time passed, the web became more and more complex, and we are facing the fact that the styles cascade turned into a problem for us. Distributed teams, working on their parts of the system, combining them into reusable modules, assemble an application from pieces, like Dr. Frankenstein, stitching styles into one large canvas, can get the sudden result... Due to the cascade, the styles of module 1 can affect the display of module 3, and module 4 can make changes to the global styles and change the entire display of the application in general.

Developers have started to think of solving this problem. Style naming conventions were created to avoid overlaps, such as Yandex's BEM or Atomic CSS. The idea is clear, we operate with names in order to get predictability, but at the same time to prevent repetitions.

These approaches were crashed of the rocks of the human factor. Anyway, we have no guarantee that the developer from team A won't use the name from team C.

The naming problem can only be solved by assigning a random name to the CSS class. Thus, we get a completely independent CSS set of styles that will be applied to a specific HTML block and we understand for sure that the rest of the system won't be affected in any way.

And then 2 approaches came onto the stage to organize our CSS: CSS Modules and CSS-in-JS. Under the hood, having a different technical implementation, and in fact solving the problem of atomicity, reusability, and avoiding side effects when writing CSS.

Technically, CSS Modules transforms style names using a hash-based on the filename, path, style name. Styled-components handles styles in JS runtime, adding them as they go to the head HTML section (<head>).

Approaches overview

Let's see which approach is more optimal for writing a modern web application!

Let's imagine we have a basic React application:

import React, { Component } from 'react';
import './App.css';

class App extends Component {
 render() {
   return (
     <div className="title">
       React application title
     </div>
   );
 }
}
Enter fullscreen mode Exit fullscreen mode

CSS styles of this application:

.title {
 padding: 20px;
 background-color: #222;
 text-align: center;
 color: white;
 font-size: 1.5em;
}
Enter fullscreen mode Exit fullscreen mode

The dependencies are React 16.14, react-dom 16.14

Let's try to build this application using webpack using all production optimizations.

we've got

uglified JS - 129kb
separated and minified CSS - 133 bytes

The same code in CSS Modules will look like this:

import React, { Component } from 'react';
import styles from './App.module.css';

class App extends Component {
 render() {
   return (
     <div className={styles.title}>
       React application title
     </div>
   );
 }
}
Enter fullscreen mode Exit fullscreen mode

uglified JS - 129kb
separated and minified CSS - 151 bytes

The CSS Modules version will take up a couple of bytes more due to the impossibility of compressing the long generated CSS names.

Finally, let's rewrite the same code under styled-components:

import React, { Component } from 'react';
import styles from 'styled-components';

const Title = styles.h1`
 padding: 20px;
 background-color: #222;
 text-align: center;
 color: white;
 font-size: 1.5em;
`;

class App extends Component {
 render() {
   return (
     <Title>
       React application title
     </Title>
   );
 }
}
Enter fullscreen mode Exit fullscreen mode

uglified JS - 163kb
CSS file is missing

The more than 30kb difference between CSS Modules and CSS-in-JS (styled-components) is due to styled-components adding extra code to add styles to the <head> part of the HTML document.

In this synthetic test, the CSS Modules approach wins, since the build system doesn't add something extra to implement it, except for the changed class name. Styled-components due to technical implementation, adds dependency as well as code for runtime handling and styling of <head>.

Now let's take a quick look at the pros and cons of CSS-in-JS / CSS Modules.

Pros and cons

CSS-in-JS

cons

  • The browser won't start interpreting the styles until styled-components has parsed them and added them to the DOM, which slows down rendering.
  • The absence of CSS files means that you cannot cache separate CSS.
  • One of the key downsides is that most libraries don't support this approach and we still can't get rid of CSS. All native JS and jQuery plugins are written without using this approach. Not all React solutions use it.
  • Styles integration problems. When a markup developer prepares a layout for a JS developer, we may forget to transfer something; there will also be difficulty in synchronizing a new version of layout and JS code.
  • We can't use CSS utilities: SCSS, Less, Postcss, stylelint, etc.

pros

  • Styles can use JS logic. This reminds me of Expression in IE6, when we could wrap some logic in our styles (Hello, CSS Expressions :) ).
const Title = styles.h1`
 padding: 20px;
 background-color: #222;
 text-align: center;
 color: white;
 font-size: 1.5em;
 ${props => props.secondary && css`
   background-color: #fff;
   color: #000;
   padding: 10px;
   font-size: 1em;
 `}
`;
Enter fullscreen mode Exit fullscreen mode
  • When developing small modules, it simplifies the connection to the project, since you only need to connect the one independent JS file.
  • It is semantically nicer to use <Title> in a React component than <h1 className={style.title}>.

CSS Modules

cons

  • To describe global styles, you must use a syntax that does not belong to the CSS specification.
:global(.myclass) {
    text-decoration: underline;
}
Enter fullscreen mode Exit fullscreen mode
  • Integrating into a project, you need to include styles.
  • Working with typescript, you need to automatically or manually generate interfaces. For these purposes, I use webpack loader:

@teamsupercell/typings-for-css-modules-loader

pros

  • We work with regular CSS, it makes it possible to use SCSS, Less, Postcss, stylelint, and more. Also, you don't waste time on adapting the CSS to JS.
  • No integration of styles into the code, clean code as result.
  • Almost 100% standardized except for global styles.

Conclusion

So the fundamental problem with the CSS-in-JS approach is that it's not CSS! This kind of code is harder to maintain if you have a defined person in your team working on markup. Such code will be slower, due to the fact that the CSS rendered into the file is processed in parallel, and the CSS-in-JS cannot be rendered into a separate CSS file. And the last fundamental flaw is the inability to use ready-made approaches and utilities, such as SCSS, Less and Stylelint, and so on.

On the other hand, the CSS-in-JS approach can be a good solution for the Frontend team who deals with both markup and JS, and develops all components from scratch. Also, CSS-in-JS will be useful for modules that integrate into other applications.

In my personal opinion, the issue of CSS cascading is overrated. If we are developing a small application or site, with one team, then we are unlikely to encounter a name collision or the difficulty of reusing components. If you faced with this problem, I recommend considering CSS Modules, as, in my opinion, this is a more optimal solution for the above factors. In any case, whatever you choose, write meaningful code and don't get fooled by the hype. Hype will pass, and we all have to live with it. Have great and interesting projects, dear readers!

Top comments (23)

Collapse
 
dastasoft profile image
dastasoft

One pro of CSS, the hot reload is instant when you just change CSS, with CSS in JS the project is recompiled. For CSS-in-JS I find easier to reuse that code in a React Native project.

My personal conclusion is that we are constantly trying to avoid CSS but at the end of the day, CSS will stay here forever.

Great article btw!

Collapse
 
greggcbs profile image
GreggHume • Edited

I ran into issues with css modules that styled components seemed to solve. But i ran into issues with styled components that I wouldn't have had with plain scss.

So some things to think about:
Styled components is a lot more overhead because all the styled components need to be complied into stylesheets and mounted to the head by javascript which is a blocking language.

On SSR styled components get compiled into a ServerStyleSheet that then hydrate the react dom tree in the browser via the context api. So even then the mounting of styles only happens in the browser but the parsing of styles happens on the server - that is still a performance penalty and will slow down the page load.

In some cases I had no issues with styled components but as my site grew and in complex cases I couldn't help but feel like it was slower, or didn't load as smoothly... and in a world where every second matters, this was a problem for me.

Here is an article doing benchmarks on CSS vs CSS in JS:
pustelto.com/blog/css-vs-css-in-js...

I use nextjs, it is a pity they do not support component level css and we are forced to use css modules or styled components... where as with Nuxt component level scss is part of the package and you have the option on how you want the sites css to bundled - all in one file, split into their own files and some other nifty options. I hope nextjs sharped up on this.

Collapse
 
kachidk profile image
Nwanguma Victor • Edited

A big tip that might help.
Why not use SCSS and unique classNames: For example create a unique container className (name of the component) and nest all the other classNames under that unique container className.

.home-page-guest {
  .nav {}
  .main {}
  .footer {}
}
Enter fullscreen mode Exit fullscreen mode
<div className="home-page-guest">
  <div className="nav" />
  <div className="main" />
  <div className="footer" />
</div>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
fantasticmrhank profile image
Hank Queston

I agreed, CSS Modules make a lot more sense to me over Styled Components, always have!

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
thekooldev1232 profile image
thedev1232

How about having to deal with libraries like Material UI with next js?

I have an issue to decide whether to use just makeStyles function or should we use styled components?

My main concern is code longevity and maintenance without any issues

Collapse
 
alienpr84 profile image
Alien Padilla Rodriguez

@Petar Kokev If something I learned from this years of working with React and other projects is that the correct library for project isn't the correct library for another. So the mos important think that we need to do is select the tools, libraries and technologies that fit better to the current project. In this case you can't use Styled-components on sites that require a good SEO, becouse the mos important think here is the SEO and you cant sacrify it.

Collapse
 
goldhand profile image
Will Farley

My big issues with styled components is they are deeply coupled with your code. I've opted to use emotion's css utility exclusively and instructed my team to avoid using any of the styled component features. We've loved it but this was a few years ago. For newer projects I'm going with the css modules design.
Also why does anyone care about sass anymore? With css variables and the css nesting module in the specification, you get the best parts of sass with vanilla css. The other features are just overkill for a css-module that should represent a single react component and thus nothing :global. Complicated sass directives and stuff are just overkill. Turn it into a react component and don't make any crazy css systems.

Collapse
 
kachidk profile image
Nwanguma Victor

Same I was trying to revamp my personal site, I discovered that I would have to rewrite alot of things, and then I later gave up. I would advice css modules are the way to go, and it greatly helps with SEO.
And in teams using SC, naming becomes an issue because some people don't know how to name components and you have to scroll around, just to check if a component is a h1 tag 🤮
CACHEing I can't stress this enough, for enterprise in-house apps it doesn't really matter, but for everyday consumer-essentric apps CACHEing should not be overlooked

Collapse
 
goldhand profile image
Will Farley

You can still have a top-level css file that isn't a css module for global stuff

Collapse
 
kibobishtrudelz profile image
Petar Kolev

It is not true that with styled-components one can't use scss syntax, etc. styled-components supports it.

Collapse
 
edshav profile image
Eduard

How about css-in-js frameworks like material-ua, chakra-ui and others? In my opinion, they dramatically speed up development.

Collapse
 
gass profile image
Gass • Edited

Good post. I've been using CSS modules for a short time now and I like it. Allows everything to be nicely compartmentalized.

I also like that it gives more freedom to name classes in smaller chunks of CSS code.

Instead of using it like so: {styles.my_class} I preffer {s.my_class} makes the code looks nicer and more concise.

Collapse
 
alienpr84 profile image
Alien Padilla Rodriguez

In my personal opinion I see Styled Components more for a Single Page Aplications where the SEO isn't important and is unecessary to cache css files. In the case of static web site or a site that must have a good SEO the Module-Css is better.

@greggcbs My recomendation is to use code splitting if you have problem with the performans when you use Styled-Components in your project, in order to avoid brign all code in the first load of the site.

Good article @sergey

Collapse
 
drbeehre profile image
DrBeehre

This is awesome!
I'm quite new to Web dev in particular and when starting a new project, I've often wondered which approach is better as I could see pros and cons to both, but I never found the time to dig in.

Thanks for pulling all this together into a concise blog post!

Collapse
 
alessiochiffi profile image
alessiochiffi

With Vue, all you need to do is add scoped to the style attribute

<style lang="scss" scoped>
.myClass {
  background-color: red;
}
</style>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mario_iliev_086178c17d1cd profile image
Mario Iliev

I'm sorry but it seems that you don't have much experience with Styled Components.
"And the last fundamental flaw is the inability to use ready-made approaches and utilities, such as SCSS, Less and Stylelint, and so on."
Not a single thing here is true. SCSS is the original syntax of the package, you can use Stylelint as well. There are a lot more "pros" which are not listed here.
By working with JS you are opened to another world.
I'll list some more "pros" from the top of my head:

  • consume and validate your theme colors as pure JS object
  • consume state/props and create dynamic CSS out of it
  • you have plugins which can be a live savers in cases like RTL (right to left orientation). Whoever had to support an app/website with RTL will be magically saved by this plugin.
  • You can create custom plugins to fix various problems, or make your own linting in your team project.
  • you don't think about CSS class names and collision. I prefer to be focused on thinking about variable names in my JS only and not spending effort in the CSS as well
  • when you break your visual habits you will realise that's it's easier to have your CSS in your JS file just the way you got used to have your HTML in your JS file (React)

In these days CSS has become a monster. You have inheritance, mixins, variables, IF statements, loops etc. Sure they can be useful somewhere but I'm pretty sure that most of you just need to center that div. So in my personal opinion we should strive to keep CSS as simpler as possible (as with everything actually) and I think that Styled Components are kind of pushing you to do exactly that. Don't re-use CSS, re-use components! The only global things you should have are probably just the color theme and animations.

Collapse
 
joetidee profile image
Joe

If you're creating design system within your app, you can cache the js files by splitting your JS bundles.

Collapse
 
doctorrokter profile image
Mikhail Chachkouski

Google Lighthouse is saddened by your CSS-in-JS 😀
Whilst such css-in-js frameworks like MaterialUI dramatically speed up development (that's true) they, from another side, beat the app perfomance because of too heavy. In that case CSS modules are more preferred.

Collapse
 
schleidens profile image
Schleidens.dev

🚀🚀

Collapse
 
andrewbaisden profile image
Andrew Baisden

Both have pros and cons I alternate between the two.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.