CSS in JS: Benefits, Drawbacks, and Tooling
This is a transcript of a presentation given at Thunderplains in Oklahoma City on November 3rd, 2017.
But… here’s what Omaha actually looks like. It’s been a great city for me to hone my craft, and I think it’s a great city to not only live, but it’s also an unassumingly great technology city with a number of great local tech events and meetups. Come check it out sometime!
I work at a great company called Object Partners. We specialize in JVM and frontend development of all sorts. Between Omaha, Minneapolis, and Chicago, we have about 100 excellent consultants.
Every presentation has this prerequisite sponsors slide, but they truly deserve so much credit. Without their support, we wouldn’t have all gotten together for this great conference, so it is sincerely appreciated. Thank you so much!
I feel like some of you may have a pretty negative perception of CSS in JS, or at least not an overtly positive perception. This can be for a variety of reasons, but I think paramount for some is that it goes against the “separation of concerns” that have been ingrained in our minds and regularly re-enforced. It can feel weird, it can feel unclean, and it can even feel like a solution looking for a problem.
My goal is to take you from this initial possibly negative stance to cautious skepticism. I hope you leave here with an understanding of why CSS in JS exists, and how it can solve some very real developmental problems. Perhaps it might not be the perfect fit for your application at this point, but you can understand why someone else would adopt some of these technologies.
A stretch goal is that many of you will leave here fully convinced that CSS in JS is the solution to some of your CSS problems, and you’ll adopt these techniques for your next application.
- Discussion of the problems of CSS
- Defining what CSS in JS is, and how it can solve some of these problems
- Discussing some various CSS in JS libraries, and real world examples of the usage of these
- Finishing up with some discussion of drawbacks of CSS in JS and some quick demos
Let’s start with a brief discussion of some of the problems of CSS. An important caveat here, these problems tend to be focused on real-world examples that every-day developers have run into, or have run into without even realizing it and resorting to hack-y work arounds. This is not to say that problems “at scale” are not important, but if we can narrow the scope to problems that every-day developers, and myself, have faced, we can more clearly define the problems in more approachable terms and concepts. In illustrating these real-world problems, the foundational basis for the creation of these CSS in JS techniques will be made evident.
For these first examples, let’s say that we’re a team of front-end developers working on a component library to be used throughout a company’s internal application suite. We get business requirements from users and they’re filtered through our UX department, who weeds these out and gives us the truly important requirements.
Our first task from the UX team to design a button component that has a hover state and raises slightly on hover. We design and build this with vanilla CSS and HTML. This button looks great, meets every current need we have, and the code is quite succinct, to boot. This is a great component!
Our UX team makes another request of our team. We now need a secondary style, and the previous button will be considered the primary style. So… we add this secondary class, we complete the objective, and we’re still feeling fairly good about our button component.
We get another request that the button is far too large for certain applications, and the request has been made that we add a button with reduced padding, font-size, etc so more can be displayed on screen. We add this
tiny class, and we complete the business requirements. This is fine…
We get a final request that the button neds to a have a hover state for accessibility and more intuitive feedback on desktop screens. We add this class, and our button is “completed,” for now. This is not fine 😫
Check out the code slide here
Our clean, pristine button component and CSS are no longer so clean. Each of these simple requests stack and increase the complexity of the component.
In effect, we’ve introduced a number of CSS globals. As anyone is aware, globals are a common enemy of any codebase, and the introduction of globals will make this CSS harder to maintain, use, and develop against. This is especially evident if designing something like a component library, where hundreds of other developers in our company will use this button, extend upon it, and introduce their own globals. It’s incredibly easy to imagine a scenario where collision occurs and this practice makes this approach untenable.
Let’s consider the usage of this button component by a developer who comes on the project long down the road, far after the team that designed the component has left for greener pastures. This developer is relatively inexperienced, and is just trying to add in style that makes a link look like a button. This seemingly simple task is more complicated than it appears at first glance. These globals make the job far more diffiult than expected, and the developer resorts to messy hacks and the use of
!important in an effort to ship and meet a deadline.
But wait… we’ve invented techniques to solve these problems!
CSS naming methodologies like BEM exist to solve this exact problem re: name collision and CSS globals. Also consider other solutions like Atomic CSS, OOCSS, SMACSS, SUITCSS, Object-oriented CSS, etc.
I am very much not a fan of these methodologies. They introduce cognitive overhead and introduce naming concerns. Is this particular thing a modifier? An element? A block? Naming is already notoriously hard; this technique certainly does solve the class name collision problem, but I contend it imposes extraneous concerns on behalf of the developer that can be solved in other, cleaner ways (as we’ll see soon).
Consider this quote from Kyle Shevlin. CSS in JS automates this safety, via the usage of tooling and APIs.
Consider this quote by Kent C. Dodds. Why would we not use tooling to automate trivial naming concerns? Development is oftentimes all about automating hard problems, and is naming not one of the most (unecessarily) difficult ones we face?
But others still will note that this problem of globals has been solved with tooling in other areas. CSS Modules and Shadow DOM (a staged spec introduced in web components to isolate styling to a particular subset of the DOM) both are intended to solve this problem.
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. Shadow modules are similarly going to be great, but I’m not quite sure they’re ready for primetime yet (nor are web components quite there in my opinion).
Shadow DOM, and web components as a whole, are certainly something that I think will continue to grow in popularity in the coming months and years. That said, the web component implementations are still slightly in flux, and for full browser support, you’ll certainly have to ship a polyfill.
The bigger your application gets (and the more CSS you write), the more globals you will create which is quite simply making your application harder to maintain and use. In order to work around these scaling issues, naming strategies or tools — LESS, SASS, etc. — are often utilized.
CSS in JS solves this problems cleanly, clearly, and simply.
Also I’d like to note here that, in general, I tend to think “does not scale,” is a pejorative used by people who are just not fans of that particular technology. That is not the case here though, as most advocates of CSS in JS love CSS. You’ll also see soon that when you’re writing CSS in JS, you’re using all the functionality that we know and love in CSS.
Your class names and styles (i.e.
.css files) are separately located to what is being styled, typically JSX with
className or in non-React projects, as separate HTML files. Additionally, as previously mentioned, as CSS is global by default, your styles could be styling unrelated functionality in your application. Jointly, these two concerns make it incredibly difficult to re-factor unused CSS.
Removing styles can be a spooky endeavour 👻, and many regression tests, QA, or manual testing is often required to validate with certainty that only certain functionality was impacted. Each of these techniques requires manual testing and validation, and is not something that every developer has available to them.
CSS in JS gives you confidence that by removing this particular component, you’re removing style code applicable to this component only.
Much credit where credit is due, many of these problems were identified in a formative CSS in JS presentation by Christopher Chedeau (a developer at Facebook), who delivered a great presentation highlighting many of these issues way back in 2014!
Early in my career, I was in a meeting where I criticized a proposed solution that I thought to be poor, and left it at that. Another person in the meeting said “If you can’t bring anything to the table, then don’t say anything at all.” While the character of the message was delivered imperfectly, the content of the message still resonates with me. It’s easy to criticize something. It’s far, far harder to criticize something, and propose something better, or that could be better.
At this point, the discussion will shift into what CSS in JS is, and how it can fix some of these aforementioned problems. In other words, let’s bring something to the table!
It’s not CSS in JS or CSS. It’s CSS in JS with CSS.
The CSS you’ve formerly written can continue to be written with this paradigm shift. The best parts of CSS are maintained, and, for the most part, the ills of CSS are remedied with these new approaches.
Maybe there’s actually some validity to this practice.
Maybe it can really improve the quality and maintainability of your application.
Maybe we will see!
But, But, But, you exclaim! This goes against everything we’ve been taught about seperation of concerns. HTML in my JS was already enough of a shift, but this is just too much!
It just feels wrong.
Consider this great slide by Cristiano Rastelli.
Consider also Vue’s single file components, which are a perfect encapsulation of this model.
Let’s start with a definition by contradiction, or in other words, what CSS in JS is not.
CSS in JS is not, or at least not exclusively, inline styles. While inline styles are certainly an example of what CSS in JS can look like, they’re not necessarily the best implementation for a variety of reasons. First and foremost, only a subset of CSS is supported, so things like pseudo styles (
:focus, etc.), media queries, and a number of other useful and required CSS functionality is not supported with this implementation model. Additionally, inline styles can be difficult to override, which makes components that use them historically harder to extend.
You can certainly go this route, and several libraries exist to allow for this implementation while adding some of these needed features back, of particular note is Radium.
This is an example of an inline style, and one of the first methods advocated for CSS in JS.
This is not what I’d consider the best way to write CSS in JS, although it does have its place (particularly if props change very regularly, or in other instances when performance may suffer with rapid changes).
As noted, you lose out entirely on some of the best parts of CSS, so this doesn’t seem, to me, to be the best implementation of CSS in JS, or the one to reach for most readily.
It’s high time to begin talking about what CSS in JS actually is. We’ll go over some high level goals of CSS in JS, as well as some common patterns and coding techniques for what it can do for a modern application.
Additionally, detail will be provided for how it actually solves the aforementioned “problems with CSS”.
CSS abstracts style to the document level.
CSS in JS abstracts style to the component level.
With naming methodologies like BEM, we can get pseudo encapsulation. With Shadow DOM, we can get true encapsulation at the component level, but this requires a polyfill in many browsers and isn’t quite at a point where it’s the perfect solution for any application.
With CSS in JS, we can get true encapsulation at the component level, today. Under the hood, a unique hash will be generated for the class name, and a real stylesheet will be created with this class. This allows us to target a unique element (a component!) today, without polyfills and in an automated way so we never again need to waste cognitive cycles constructing meaningful, isolated class names.
A real stylesheet gives you the best parts of CSS (media queries, pseudo styles, flexbox, etc.).
It lessens the bad parts of CSS (globals!) by scoping to a class name
If you already know CSS, great! CSS in JS presumes that you do; use the properties, rules, etc. of CSS that you already know and love continue to function just as they do in vanilla CSS.
As an example, here is how styled-components (a library we will soon talk about) constructs a real style sheet. A template string is passed to the library containing the CSS styles. From these styles, a unique hash for a class name is created, and applied to a style tag in the head tags of your application. This constructed class name is automatically applied to your component!
It’s an automated process to a previously manual problem, and you get the benefit of writing real CSS!
CSS in JS brings CSS into the component era.
Additionally, we are creating distributable, single import components that are entirely encapsulated. No configuring of loaders, no loading of additional stylesheets. One import, highly shareable, highly consumable, and highly simple!
CSS in JS gives us truly unlimited semantic elements.
HTML5 gave us
aside, and others.
CSS in JS gives us truly unlimited semantic elements that are semantically clear at a glance:
Copyright, the possibilities are endless.
At a glance, each element’s intent & purpose is incredibly clear. On the left, some of the element’s meaning is clear. It’s clear that semantic elements have meaningful value, and the fact that CSS in JS gives effectively unlimited semantic elements is an underappreciated win!
The left is the mental model we must keep in our minds when writing CSS centered around HTML. We must be aware that a class we apply in HTML is styled with a class name. We must be aware that these class names can cascade and stuck, sometimes interfering in unforseen ways. These cacading rules then become stylesheets in our DOM that can themselves conflict.
The right is the mental model when writing CSS in JS. Write HTML (JSX) that is encapsulated with component-scoped styles. No stacking. Never worry again about CSS rule specificity (and no
!important hacks), and truly think in components!
The case for CSS in JS has hopefully been made apparent. These techniques solve real problems of CSS. But, how do they solve them? What libraries exist to implement these CSS in JS techniques, and what does writing code in each of them look like?
First, a caveat: most of these libraries are tied to React. However, not all of them are tied to React, and I’ll specifically point out libraries that do not require React.
In general, libraries that simply export a className hash (which can be used as a
className in the consuming component) are typically framework agnostic. Some of the most interesting libraries expose several libraries, some of which can be used in any framework, and others that specifically target React.
styled-components is what I would call the “gateway drug” to CSS in JS libraries. You author using template strings, so you can write CSS not as an object, but as a string and dasherized just like it’s authored in CSS.
This means that styled-components is probably the easiest to get up and running, and I would recommend it to get your feet wet with CSS in JS techniques. In addition, it’s a great choice for beginning to move off of a formerly vanilla CSS code base, because you can generally re-use most of your existing CSS, with some small modifications here and there.
The css helper constructs a “mixin” that can be re-used and applied when needed. This can be particularly helpful to encapsulate rules, and then use them conditionally, when required.
Additionally, you can see here another of styled-components’ great features. Prop injection! This means that props can be passed to these styled components, and then parsed and style rules can be conditionally applied/removed. Very cool!
Glamorous built on some of the ideas of not only styled-components, but also glamor, the library that powers much of Glamorous’ underlying functionality.
The central difference with Glamorous is that is expects objects (similar to the kind passed to inline styles), but that can also be merged with subsequent objects and that can also accept things like media queries, pseudo styles, etc.
It might seem more natural to begin using styled-components, but as of late, I’ve found myself increasingly drawn to and really liking the functionality available in Glamorous, particularly with the style objects. Merging, conditional applying of rules, etc. feel very natural with Glamorous.
Note: glamor, the underlying library, is framework agnostic!
We can begin to get a solid feel for Glamorous’ API. The function takes 0 to n objects (or functions that return an object). Any function is injected with the current props passed to the component, as well as a global theme prop if tying into Glamorous’ exposed theming capability.
The code here is using the object rest spread syntax, which makes the code slightly more terse.
Emotion is another excellent library that feels very similar to styled-components because it also allows for template literals to inject styles. The key difference, and a particularly interesting idea, is that it ships with a babel plugin which attempts to pre-compile the styles that aren’t dynamic, thereby reducing the payload of the resulting bundle.
In general, I think the ideas of minimizing (or removing entirely!) a runtime are particularly interesting, and will remain an area to keep an eye on as the community matures and develops further processes and tooling.
As you can see, the code feels very similar to styled-components. If you’re looking for a similar API but with some other benefits (and trade-offs), consider emotion as it’s a very solid alternative.
Polished is a framework agnostic collection of utility methods for CSS in JS functionality. Some have described it as the “lodash of CSS in JS,” and that’s a very fair comparison.
Many helpers/mixins are provided for usage in any CSS in JS library, and functionality such as
rgba, etc. can be utilized in your application to do some really useful things.
Polished is particularly useful in adjusting color (hue shifting, adjusting transparency, darkening colors, lightening colors, etc.), but contains a number of other additional utilities.
These are just the color methods, but polished contains a bunch more including:
- em/rem helpers
- radial gradient generators
- normalize.css injection (CSS reset)
- shorthands for common things such as text-overflow ellipsis, font-face, etc.
If I had more time, I’d love to cover every major library that can be used. Unfortunately, in the interest of time, I’m only able to cover a few. That said, there are so many libraries in the frontend ecosystem that can be utilized.
Also note that every single one of these libraries mentioned (in this slide) is framework agnostic!
It can be helpful to consider library downlaod count, but that is in no way, shape, or form indicative of a library’s quality or usefulness for your application.
That said, it can be valuable just to get a baseline of relative support, community activity, etc. which can be a metric to consider when targeting a CSS in JS library to use in your application.
- styled-components and glamorous are two of the most popular and “hot” choices
- glamorous requires glamor, so a lot of the glamor downloads are likely from glamorous (but note: glamor can be used outside of glamorous)
- aphrodite and radium were huge players (and may still be a good choice in certain scenarios), but I tend to like the others a bit more
- radium is inline style based, but adds pseudo styles and other expected CSS functionality while still using inline styles; however, it’s not quite as utilized or “in vogue” as it may have once been
Similar story here, styled-components and glamorous are on the rise, while the others are relatively constant.
The cost of using these libraries is non-null, even when using babel plugins or other techniques to reduce the file size as much as possible.
However, the cost is relatively small, so weighing developer utility and other benefits of CSS in JS vs. a relatively small size seems to be a fairly inoccuous concern.
Seeing some of the companies that are utilizing these technologies lends some validity to the practice and makes it more obvious that there’s some real value being generated by the usage of these libraries.
I think it’s important to actually show some real-world usage and common patterns for writing “real world” code in each of these libraries.
Common things like theming, utilizing props, inheriting/composing styles, etc.
Note: I’m not endorsing any particular library, so I’ll jump around a bit to give you a better idea of what each library feels like.
Props injection is a natural, React-y method of altering a given component’s style under certain conditions. For instance, if we were to re-visit our earlier CSS only button, we can create the same with props, but with complete encapsulation to that single button component.
A common question is that one of CSS’ great features is inheritance and easy extension of base classes. Inheritance is typically accomplished in these libraries by injecting a previously styled component (😉) and adding additional styles, as needed. All previously defined styles will be merged with, or possibly replaced by, the new styles.
One final reminder that in each of these libraries, real CSS and real stylesheets are added to the DOM. The value of CSS remains but with the clear and numerous benefits that CSS in JS provides.
But what about animations? Those inherently require a global animation name!
Most libraries include some type of helper utility to return a unique identifier/hash for those animations so that globals remain stripped from the code base.
If you’re designing a distributable component and want to make it CSS in JS friendly, consider exposing the className prop for consumption.
- Most libraries inject a className — or provide a className — which would then be applied on top of the existing styles
- Additionally, if you are “wrapping” your styled elements, this is a great way to make those wrappers extensible, as well
Libraries that require a certain class structure (e.g. Bootstrap), can also work with CSS in JS libraries!
This example exposes a wrapped bootstrap Alert, which can then be used as a regular React component with a prop specifying the type of alert (e.g.
Some of the time, it may be necessary to inject globals, in particular when targeting
For these times, most libraries include a mechanism to inject a global into a DOM style tag.
Nesting is supported in most of these libraries, but it can be seen as a kind of anti-pattern in certain circumstances, especially if you’re targeting a child selector.
However, that said, styled-components documentation mentions that:
… Used sparingly it’s a great way to lighten your code by reducing the need to create explicit classes for every element.
So use with caution, but it can be a decent technique to lighten some of your component’s styles!
Theming is a particularly common use case that can be semi-difficult to architect cleanly. Most of these major libraries expose a
ThemeProvider component which can be used to provide a theme (via context) to each styled component.
It is, for instance, incredibly easy to make a light/dark theme for an application, or any number of color variants.
Check out the code slide to see the full, navigable example
To actually add theming to an application, the process is quite simple:
- Import the
ThemeProvidercomponent that most libs export
- Wrap your base component (e.g. App.js) in a
- Provide a
themeprop to the
- All contained components will be injected via props/context with the current
However, there aren’t any silver bullets it seems in frontend web technology. While CSS in JS may seem like a great fit for most applications, there are certainly some drawbacks that are worth considering.
0.2% of users may not seem like much, but if you’re Facebook scale or driving a lot of traffic it’s a concern
- 1,000,000 monthly users means 2,000 users may not be getting a usable site
How can we fix this?
You can mitigate with server side rendering (something like next would be terrific) or statically rendering to HTML (with something like Gatsby)
Rich Harris, creator of such tools as Rollup, Buble, Svelte, etc. raises an interesting point. Not only are the styles not scrapeable, but the styles can be hard to query, as well. Things like e2e tests or integration tests should not be pointed to a unique hash, and so it’s certainly a best practice to either use one of the existing babel plugins for most libraries that adds a humanized class name, or manually add your own!
Editor tooling is still in its infancy, but as CSS in JS continues to grow in popularity, I think we’ll see marked improvement on this front.
It seems like every week there are new developments to get this working as seamlessly as possible. I believe this to be a space that will continue to see rapid improvements.
As with anything, if you’re directly injecting user input (even into CSS!) you open yourself up to issues
Check out this great article from React Armory
Performance can be a concern, but I’d urge you here to not prematurely optimize. The difference between each of the libraries is arguably minimal, and the difference between CSS is relatively minimal as well.
However, if you’re pushing Facebook-scale™, or after measuring your application’s performance, then it may be worth re-visiting whether these libraries are for you, or whether there are performance optimizations you can make to improve perf.
This benchmark measures the mount time of various CSS in JS libraries vs. inline styles (i.e. without a library).
Any benchmark should be taken with a grain of salt. It is incredibly difficult to measure real-world performance, so most resort to doing large-scale operations (e.g. rendering a listview with thousands of rows, re-rendering a large table, etc.), which are generally not at all indicative of the type of application most are building.
They can be helpful to get a general idea for performance, these are not necessarily what you’ll see in real world applications.
So what should we make of all of this?
- CSS in JS solves very real problems of CSS
- It does so in a clean, component focused, and developer friendly way
- It abstracts the CSS model to the component level, rather than the document level
If I were to give a call to action, or best advice for getting started, I would give this:
styled-components as a starter (or final!) CSS in JS library
- It tends to be the most approachable as it uses actual CSS syntax, rather than style objects
If you’re a fan, consider experimenting with other libraries in the ecosystem; who knows, you may like them even more than styled-components!
Overall, I’m very enthused with the direction that CSS in JS is taking and the things it’s doing for the ecosystem. I think it provides very tangible benefits to any application, particularly in the approach it takes to solve some of the problems of CSS now, and does so in a way that feels like a real improvement over authoring in CSS.
If I were to start a new project right this moment, I’d author it using one of the CSS in JS libraries we’ve talked about, and I’d feel very enthused with that direction.
The React community can certainly inspire what many have called “selection anxiety.” The proliferation of libraries, techniques, etc. can make it incredibly hard — especially as a beginner — to know what choice to make, and whether the choice is most correct.
To help alleviate this, I recently created what I’m calling the “CSS in JS Playground,” which is a live-editable comparison of some of the most common CSS in JS libraries.
My hope is that this tool will help each of you get a feel for each of the libraries, and hopefully empower you to make a more informed decision for what is best for your particular app and use cases.
- Live editing and near instant preview of the code changes (using a web worker and the buble transpiler!)
- Service worker integration for better offline support
- A rudimentary file system! Add a file, and import it in the main index file!
- Persistence! Update any file (or add additional files) and you can share that URL with your friends
- Theming by way of a light/dark theme
A CSS in JS playground demoing some common libraries including styled-components, glamorous, emotion, and others!css-in-js-playground.com
css-in-js-playground - A simple playground for CSS in JS solutionsgithub.com
One of the best ways I’ve found to keep up with the latest technology updates, as well as just edify your current knowledge of common front-end (read: CSS in JS!) knowledge is to follow active community leaders on Twitter.
Each of the people listed here are worth a follow, as they have some really interesting, great things to say and share about CSS in JS — amongst many, many other topics!
This talk, and so many other things in the front-end community, would not be possible without the work of so many others. To them, I am so very grateful. Hopefully I, too, have contributed to the conversation!
If I have seen further, it is by standing on the shoulders of giants
One final thank you to the organizers and sponsors of Thunderplains, and for giving me an opportunity to present at my first ever conference.
I’d like to thank Phil Plückthun for some great advice on the slide content(and for the awesome idea of slide screenshots mixed with slide notes for a blog post!)
… and finally, I’d like to thank everyone who attended my NebraskaJS meetup, particularly those who provided some invaluable feedback.
Finally, I’d like to thank each of the attendees. Without you, each of us would be speaking to an empty room, and where’s the fun in that!?
This talk was originally published at Object Partners, Inc.