My very own React inline style library

Tomas Carnecky
5 min readDec 8, 2015

--

(not that there wouldn’t already be enough…)

Over the past six months I was working on a couple different projects. We were using React to render the web pages, but pretty much every project was using a different system to manage styles: plain CSS, SCSS or inline styles (with the various forms of integration into React). Based on this experience I came up with the ideal system and a sample implementation of it.

Note that the system outlined in this article is only applicable to React when targeting the Web. React Native projects have different requirements and needs.

Determining the output target

It is clear to me that the output should be CSS, and not inline styles attached to DOM elements. The simple reason is that certain features are not available in inline styles: Media queries, pseudo classes and pseudo elements.

Sure, you could emulate almost all of those in the JavaScript code once the web page and all your application code is loaded. But in the interest of a good user experience you want to pre-generate static pages so that they load quickly. And there you have no other choice than to generate CSS and reference it from the web page.

Note that I didn’t say in which form the CSS should be referenced. You can write it into an external file and use <link />. Or you can inline it into a <style> tag. Either way, the output is plain CSS.

What language should we author styles in?

Now that we’ve determined what the output format should be, we need to talk how we want to author the styles. The system should be smart enough to allow reusing third-party React components. This is hard because CSS is essentially a global namespace in which identifiers (e.g. class names) need to be allocated. The only way how that can work is if the identifiers are automatically generated. Any DSL where users can allocate their own identifiers can be ruled out because it is open for conflicts. That means we can drop CSS and any language which merely extends CSS (such as LESS or SCSS).

While CSS is a wonderful DSL for writing style declarations, it leaves a lot to be desired. We’ve seen again and again people trying to extend the language and add constructs which are already available in general purpose programming languages: control structures (conditions, loops) and composition (@extend, mixins etc). Writing style declarations in plain JavaScript is often not a pleasure, for example you have to wrap values in quotes. But what you get back in return is more than worth it: Use the same, familiar language that you also use in your React components. Get immediate access to control structures. Easily compose different styles. And the ability to share theme settings (colors, sizes etc) between React components and styles.

The style should be placed as close as possible to the React component that is using it. The majority of styles that I write are just a few declarations. It is cumbersome to have the style and element which is using it placed in two separate files. That forces me to come up with a unique name, which usually ends up being some variation of the React component name. But in cases as simple as {flex:1} I don’t want that overhead at all, it’s much simpler to just write that as inline style on the React element.

Authoring the style using React inline style declarations also makes it easier to refactor your code. You can just move the component and don’t have to worry about any additional dependencies which you need to import in the new location.

The goal: make React elements self-contained

Another way to view it: React elements are descriptions of the markup and behaviour of a DOM element. But when we use className, we add an external dependency which needs to be carried along in a side channel. What I want is the React elements also include the full description of their style. This would make React elements truly self-contained!

I do understand that it’s not always possible. Some elements will have external dependencies, such as <img /> tags or even the styles themselves when they reference a custom @font-face. However I claim that in the majority of the cases the style is not that complex and can be easily attached to the elements.

A single integration point

Integrating such a system into existing React projects is surprisingly easy. There is only a single point which we need to change: Just before rendering the root element into the DOM, we iterate through the tree, replace all inline style objects with a generated class name, and emit a corresponding CSS rule into a StyleSheet.

Inline Style Emitter

I’ve implemented the ideas outlined above in a library: inline-style-emitter (also available on npmjs). It currently only implements a DocumentEmitter which inserts the CSS rules into a CSSStyleSheet object. A corresponding emitter which stores the CSS rules in memory and later writes them into a file (e.g. for server-side pre-rendering) should be trivial to write.

To integrate it into your React project, you only need to make these changes. Pretty simple, eh?

import {Handle, DocumentEmitter, processStyleProperties}
from "inline-style-emitter";
// Create a Handle which manages all interaction with the DOM.
// Do this during startup and keep the Handle around forever.
let styleEmitterH = new Handle(new DocumentEmitter(document));
// Somewhere in a project you'll have a place where you
// call ReactDOM.render(el, container). Replace that code
// with:
let newEl = processStyleProperties(styleEmitterH, React, el);
ReactDOM.render(newEl, container);

Generating the globally optimal output

Because we have access to the exact set of styles which are used on a page (or a set of pages, if we’re pre-rendering the whole site on the server), we can use that information to generate the globally optimal output. First of all, we can be sure that we don’t leave any cruft and unused rules in the CSS output. Everything that is in there is actually needed by the pages. It also opens up the possibility for more complex global optimization strategies. For example, we could try to find the smallest set of non-overlapping CSS rules and rewrite both HTML and CSS so that the combined output has the smallest byte size. But that’s a topic for another article.

--

--

No responses yet