A look at two different writing styles for CSS-in-JS: imperative and declarative
styled-jsx lets developers write CSS-in-JS in a more declarative fashion compared to other CSS-in-JS solutions. In my opinion, it leads to code that's easier to understand.
Before I tell you why I prefer styled-jsx, let's learn about some programming concepts—It'll all make sense later, trust me!
Next: What does it mean to write 'more declarative' code?
There are two styles of expressing data. The most common way is to simply write it out as a data structure. We can call this a declarative style of writing, where we try to express logic without providing any instructions.
There are cases when this style may not be flexible enough, so some systems take an imperative approach. In contrast to the declarative style, it's a piece of code with instructions that runs procedurally. Here's an example of a Ruby gem specification, which defines a Ruby package's metadata.
You can do the same thing above with imperative code.
Next: What's the difference?
Ruby's gem specification style is imperative because we're issuing instructions that are to be ran sequentially. It can keep variables, call functions, and do all the things you can do in a Ruby program. Think of the Ruby gemspec as a program talking to the system:
On the other hand, JavaScript's declarative style isn't concerned with any instructions or control flow. It's not a piece of code that talks to your system, it's really just a table of keys and values.
Next: What makes imperative-style different?
Imperative style gives you the power to write expressions that you can't easily do with a declarative-style conventions. For instance, here's an example gem specification where the list of files are gathered via a system command (git ls-files
).
Next: What makes declarative-style different?
In contrast, here's how it may be declaratively defined in a JavaScript package's package.json
. JavaScript's declarative approach may be less flexible than the imperative one, but the constraints of a JSON format makes things more predictable and easier to glance.
No instructions here. The package.json format is really just a list of things, there are no imperative instructions to be executed.
Next: What does this have to do with CSS?
CSS is beautifully declarative. To write CSS is to write a list of rules, not a set of instructions. For instance, we would say buttons are supposed to be blue, rather than turn all buttons to blue.
Declarative: Writing "buttons are supposed to be blue" in CSS.
Imperative: Writing "please turn all buttons to blue" in JavaScript. (Please don't do this.)
Next: What about CSS-in-JS?
At first glance, we can say that most CSS-in-JS solutions require you to write in a declarative style, since it's mostly just taking CSS and putting it in JavaScript. Here's how you would define a blue button using styled-components:
Declarative CSS-in-JS: No imperative logic here, right?
However, how would you start making red danger
buttons? With styled-components, you'll have to provide a function which returns a CSS fragment depending on how you would interpret props
:
Using styled-components: Using some JavaScript logic to generate some CSS. That is, CSS-in-JS-in-CSS-in-JS.
We're now mixing the declarative nature of CSS with some rules that are written in an imperative style. We're also now interweaving 2 languages together—CSS and JavaScript—where your brain may have to switch contexts mid-way.
Next: Let's look at how styled-jsx solves this.
styled-jsx lets me write CSS in a declarative fashion. It also minimizes the interweaving of CSS and JavaScript code. This, in my opinion, makes styled-jsx code easier-to-understand, even at the modest cost of a little extra verbosity.
How would you style a danger button differently in styled-jsx? Just use CSS classes as you normally would. Simple!
Logicless CSS-in-JS. In this example, we simply declare a class rule with
.button.danger
, just as we would with regular CSS. There's no need for the props to be parsed from the CSS block.
Since writing this article, I've decided that I like the simpler approach of just using CSS modules. The reasons for that is the same as what's in this article: it's as declarative as you can get, and it's compatibility with existing tools is so far unbeatable.