React is changing the way we think about and write CSS. Today I gave presented a talk at Reactathon 2018 in San Francisco about 5 new Super Powers that you can gain by using CSS in React. These are Automation, and making CSS projects more Sharable, Adaptable, Scalable, and Maintainable.
Here is the video recording of the presentation. You can also check out the slides for this talk.
We’ve come a long way since 1993
I love CSS and the community around it. CSS is 22 years old. How many technologies created 22 years ago are still relevant? It’s really amazing to me that the CSS community comes up with innovation after innovation to keep CSS moving forward.
I remember when CSS Zen Garden came out and I was like “Woah!” What is this?! I can just style the document using one CSS file and my page looks completely different! That was when I initially got super excited about CSS. The concept that hey, I have this website, and can write one stylesheet and will have one look, then if I apply a different CSS file to it, it will have a completely different look.
Where we are today
But today, we’re not laying CSS files over a simple document. We’re working with complex applications. Not only that, but many of us are working in companies where different developer teams own different parts of the application. We need to be able to take a chunk of an application (including its styling) and share it with another team, and another chunk of it and share it with another team.
CSS in React lessons
When React came out I was immediately sold. Building in components just made sense. But, while your JS is nicely compartmentalized, your CSS still requires so many systems and so much hand-holding to get it working across projects. It’s also scary that, at any moment something can break along the way.
State of affairs today
We are creating a brand new way of developing web apps thanks to React. But from a CSS standpoint, there’s a lot to figure out. The industry is in a very experimental phase and it’s moving fast, kinda feels like 1993 again! There are currently 59 different libraries to style your React components and frankly, there’s controversy around the whole CSSinJS thing.
The reality is, you could keep your CSS and React files separate and you’ll run into problems. You can bring CSS into React and you’ll still have problems. And, you need to know CSS, and know it well. CSSinJS is not an escape hatch for disregarding the language.
But, if you do bring CSS into React, you’ll inherit a few superpowers. These are some of the more exciting innovations in CSS that I’ve seen in my career.
The Five types of new powers that come with CSS in React are discussed below in the context of 5 CSS in React frameworks.
With CSS in React you’ll get automation, and it’ll make your projects more sharable, adaptable, scalable and maintainable.
Giving up control
What we’ll discover is that when you give up some control, you will actually end up gaining more control.
Let’s explore this idea!
Automation
I don’t know about you but this is how I feel when I’m reading and writing classnames by hand in CSS these days.
This quote rings true for us CSS developers. Naming is things i super super hard.
There are only 2 hard problems in Computer Science: cache invalidation & naming things. Phil Karlton from Netscape
But, by now most of us already use some type of name-spacing convention to organize and scope classNames such as BEM. Now let’s see how automation helps reduce this cognitive load.
CSS Modules
CSS modules lets you keep writing your CSS in your CSS files. Using the help of Webpack, we import a JS Object from that CSS file into our JS code. That variable is an object, with a key mapping to your classname, and the value being an automated BEM style classname. This is where the automation comes. If you give up some control you’ll get this for free. And now your CSS is scoped to the JS file importing it.
.btn {
color: #fff;
background: pink;
}
import styles from './button.css'
console.log(styles)
> {
“btn": "button_btn_2Yt"
}
So let’s see how you would use this in a React project. We import a library called React-CSS-Modules that provides an HOC helper.
You write your button class, but you decorate it with CSSModules. And you pass it the styles we just imported. Then instead of classname
we use styleName
. So instead of saying classname=
btn, all you have to do is say
styleName=btn`.
The automation injects the CSS into the browser for you in a Style tag and attaches the generated className to the HTML element. So your end result is automated BEM generated classnames for all your components without having to worry about anything.
.button_btn_2Yt {
color: #fff;
background-color: pink;
}
<button class="button_btn_2Yt">Buy</button>
DeCSS
DeCSS introduces a different way of using CSS Modules with React. You write your CSS still in CSS files. But now instead of importing strings, you import a React component. This is like a blend of Styled Components and CSS Modules. Here’s the code example for a button. Notice we capitalized B in Button in our CSS file. This helps signify we are creating a JavaScript class, not just a css class.
.Button {
color: #fff;
background: orange;
}
import React from "react";
import { Button } from "./Button.css";
const MyButton = ({ children }) => <Button tag="button">{children}</Button>;
The rendering is very much identical to how CSS modules works. The difference is that we have now automated the creation of the React module. To get DeCSS working you just have to install the Webpack loader and add it to your configuration file like this:
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'decss-loader/react',
'css-loader?modules
]
},
]
}
Sharable
We’ve probably all the experienced working on a team, you write some CSS code and you realize there’s another team who’s also writing something similar - Maybe we can share it with each other instead of duplicating efforts? Well sharing is pretty easy with JavaScript, thanks to NPM now we can share stuff easily. But with CSS there’s no standard. Nothing is automated, and it’s a ton of work.
We end up with things like Bootstrap. It’s an amazing project with 120k followers on Github. CSS is so hard to share, so we end up with monolithic coupled setups. It would be nice to share smaller pieces or even micro level CSS with each other easily via NPM.
Classnames are the Glue
Classnames are basically the glue between your style rules and your React code. We manually create a 1:1 mapping of these ruleset strings in our code using classname="foo"
and as your project grows these connections grow and it just gets more and more complicated. So putting things into place, moving things around and refactoring is incredibly manual and tedious.
JavaScript solved the same problem using JSX
So for some context, lets look at how the JS community fixed a similar problem. Say we had a button, and it needed to do something. We would reference it using and ID.
<button id="btn">Buy</button>
$("#btn").click(() => {
// take money
}
- We then noticed a couple of issues. The
btn
string is global & needs to be name-spaced. - We’ve glued the HTML and JS together using that string value. Sharing this code requires 2 files, and instructions on how they should be linked.
Then React, said, guess what? Let’s just mix HTML and JS. Now we can bundle and share the whole thing very easily. Refactoring is also a breeze now.
And guess what? CSS has the exact same dilemma as you can see. Classnames glued to HTML.
<button className="btn">Buy</button>
.btn {
color: #fff;
background-color: pink;
}
So, what if We decide we don’t want this glue and gave up that control? We already have HTML in our React components, we have state in there. So why not add styling?
Styled Components
When components contain styles, we get the power of being able to move them and share them really easily. We can also hide their internals making them easier to refactor down the line.
Styled Components uses the tagged template literal syntax of ES6 — eg“, which is super handy. It means you can literally paste your existing CSS code between them. You get back a React component. Rendering it gives you scoped classNames
and injectedstyles
.
Sharing Components along with their styling is now easy
Let’s say we’re using an external library, like we’ve just installed something called Shared
. In Styled Components, just like we style HTML elements, we can style React Components as well.
So now we can create a new component, say we call it Wrapped
and since our styles are colocated with the markup, no classname is needed. It’s all in the component itself.
So when we render Shared
we get the base styling. And when we render the Wrapped
version we get the color pink that we added to it.
So you can see, if we let go of the control of using classNames and style within a component, sharing can now be done using one file, and published to NPM.
JSON vs. CSS
The idea of Styled components was adapted and extended by the Glamorous and Emotion libraries. Glamorous is different in that you create components using JSON style notation instead of real CSS strings. This also means they use a different CSS parser under the hood (glamor vs stylus). But everything else is quite similar. And the end result is quite similar as well. So it is really a matter of preference.
Adaptable
Let’s see how CSS in JS makes it easy to create code that adapts to its environment.I’m going to show you 3 examples of this.
Styled System
Styled System was built on top of Styled Components. It adapts to inputs and is great for setting up layout in new projects. In Styled System you a subset of CSS made declarative and responsiveness is built in.
In this example we’re defining an array with 3 potential widths for this box, at the smallest break-point it’ll be 100% width, the next breakpoint and up it’ll be 50%, then 25%. So you can see if this responsive array was going to be shared across all you would have to say is width
is responsive and hide away how that is implemented.
Styling App State
Next, adaptability comes into play when we’re talking about state change, because we’re now able to avoid writing one liners with inline styling. We can combine our basic CSS code with state based toggles in one place.
Let’s say a design team in your company gives you an input field with a 5 pixel padding. And then you decide you need a password input field. You can call an extend
method - it’s kind of reminiscent of jQuery, you can chain these calls, so you can pass it an attribute object, in this case we want to add a placeholder string to Input
.
Next we change the border color, and here’s where it gets more interesting because we can now access our props
. If we get an error we can change to a red border, and our default color is black. So this is an example of how clean it is to share an element with styling, and read the styling logic down the line. As a bonus, since we are in React and already doing DOM diffing, changed CSS rules, are more efficient. Specifically a list of the changed styles is gathered and only those changes are applied via the CSSOM.
DeCSS usage
Toggling style rules based on props
in DeCSS is a breeze. All you have to do is declare a modifier
rule using the form -modifier
so in this example, .Button-danger
in your CSS file. Then in your React component, you have prop based rules, so passing in <Button danger>
will automatically apply the modifier rule to your component. Just that simple!
Global Styling
And finally, CSS in React is also adaptable in the sense that if you want to make changes across your entire app, globals is still an option. You just use the injectGlobal
helper in JS, or use :global
in CSS.
Maintainable
How we end up with Dead & Unused CSS
Traditionally we need to deal with Dead and Unused CSS in our projects. Let me give you a quick example.
Let’s say we have this simple setup where we have an input and there are two variations of it on the page. We’re importing a simple CSS file and we’re rending both of them. So the browser gets both of those styles in a CSS file and everyone is happy. Time goes by, and the product changes, and its decided that we don’t need the second input box on this page anymore so we stop using C2.
So the problem is that since we’re blindly importing the CSS file, the C2 styling is still on the page, and that’s what we would call Unused CSS, which bloats your initial page size, and affects rendering speed. It’s not good. It also doesn’t go away automagically.
Then, it can get worse. Let’s say we decide “you know what, we’re just going to delete this component”. If you don’t take the time to go and find the matching CSS, and remove it from your codebase, that’s when you end up with Dead CSS. And it’s almost impossible to remove later because you’ve lost the connection.
There’s a saying that goes “if you don’t use it, you lose it.” And now that works in CSS in JS. “If you don’t use your CSS, you lose it!”.
Automatic Dead CSS Elimination via CSS in JS
Another super power of using Styled Components - or any of the CSS in JS Framework really - is that in the same exact scenario, we never have dead CSS. Remember how we removed the classNames glue? Now if you don’t render a component, its styles will not be injected into the DOM. Meaning you get free unused CSS elimination. So if we stop using C2 - we don’t inject its CSS. If we delete C2, we don’t even ship it’s CSS.
Automatic Critical CSS via CSS in JS
Critical CSS says hey Please “don’t give me CSS for stuff I’m not rendering on the page.” To achieve this on a site using CSS files, is very tedious and manual process. It is possible though. But in React, we only render what we need, and since our CSS is in JS, we only render CSS that we need. We only deliver CSS that is critical to the initial render.
Snapshot Testing CSS using CSS in JS
Writing tests for your CSS is a new idea that is taking off thanks to CSS in JS and tools like Jest. It’s great for regression testing user interfaces and is really handy when you are building things like design systems where styling rules are by definition not changing often. Let’s take a look at how to do this using Styled Components for example. First install the jest-styled-components
library. It gives you a few of nice features such as being able to test individual style rules.
Here’s a simple test where we want to make sure the button renders correctly at the time of creation. We have a pink color assigned to the button. We create a Snapshot and can match the Snapshot. You can now assert using “toHaveStyleRule” that for example the color styling on the element is set to pink. We can also do full and shallow renders using Enzyme.
Sacalable
As your product and business grow in scale, there are features of CSS in React that can be life-savers.
Server Side Rendering & Page performance
Serverside Rendering CSSinJS refers to when your Styles are written in JavaScript the browser no longer has a CSS file to load or cache, which means the browser has to wait for the JavaScript bundle to download, parse the JS, inject the relevant <style>
tags into the head which takes time, and during this time you may even see some un-styled content flashing.
To avoid this extra wait, you should include your styles inline, along with the string version of your React components. Just as you call renderToString with React, you call renderStylesToString in your CSSinJS flavor of choice, in this case emotion-server
. This function returns all a string of style rules for the main entry point.
This does a few thing. First user who have JavaScript disabled, will get your HTML and CSS, which means they can at least see your initial layout. Also, since the browser has enough information to render that content, the Time to First Render (user’s seeing something appear visually) goes down. And as mentioned before these styles are limited to the bare essentials, nothing more than what is needed, no junk.
Once the browser is done downloading and parsing the packaged JavaScript bundle, Emotion takes over. It removes the original style tag from the DOM. It then injects the style tags using the JS insertRule into the head. And at that point you’ve reached Time To First Interactive.
Now here’s the fun part. Since now we’ve returned the entire usable page as raw content content, we’ve met Google’s Accelerated Mobile Page’s requirement. AMP pages can not reference external stylesheets, all styles must live in the head of the document. So this is a huge bonus. That requirement is met for free.
Right To Left conversion in CSS
When and if you start scaling into international markets, i18n is something you will naturally have to deal with. Along those lines, LTR styles need to be mirrored for LTR customers. For example say you have a content block with left padding, it should be changed to right padding, etc. If you’re using a library that supports the JSON style format, like Glamorous or Emotion, you’ll get this power for free. rtl-css-js
is a simple utility library that takes in a styles object, and inverts horizontal style rules. An otherwise complex problem can now be addressed quite easily using the power of JavaScript.
Themes
Next we’ll look at theming. There are many reasons to have a theme. If you do the upfront work of planning, extracting, and gathering data to create a theme, you’ll save so much maintenance and refactoring time in the long run. And there’s just so many things you can do with themes. You can let the user switch themes or create their own themes. You can use color functions, to create a theme for the colorblind for example.
Creating themes in CSS in JS is super simple. It’s literally just a JavaScript object that you pass to the library through a helper wrapper called ThemeProvider and that uses React context to populate to its call sites.
In this example we have a dark theme and a light theme and Glamorous takes that theme and applies it to this element. And when we render our app all we have to do is provide that theme to it. So we will either end up with a dark box in this example.
React Native options with CSS in React
Using CSS in React, means you’re already on your way to styling not only components for use on the web but also in Native apps.
Here is a simple Hello World app written in React Native and Styled Components. You can style React Native primitives like StyleSheet, View, Text, Image, Animated, and Touchable and style them using styled components.
Conclusion
In closing, this tweet from Guillermo Rauch is insightful.
It really does feel like we’re in the first quarter of a long game, that may go to overtime when it comes to CSS in JS. We will keep seeing lots of score changes, substitutions, and end hopefully with a much stronger team at the end.
Github Examples Repo
I’ve also built and updated a CSS-in-React repo on github with a frameworks including CSS-Modules, Glamor, Glamorous, Emotion, Stylable, DeCSS, and JSS. So if you are curious, just clone the repo and play around with the options to get a feel for what you works for you. At the end of the day, these are just tools, and will vary depending on your personal needs and preferences.