Getting Emotional with EmotionJS
For the past few years I work for Tikal — a consulting company providing premium consulting services to Hi Tech companies. My area of expertise is building large scale, production ready Frontend applications.
Lately I have been doing consulting for a client that uses EmotionJS as their styling technology. They needed to set a design system, so I chose to use the theming mechanism that Emotion offers.
From this point on, unforeseen challenges and adventures awaited me, in this unexplored land of Emotion.
What is Emotion?
So first, as I do with every technology I start working with, I did a little research about Emotion:
Emotion is a CSS-in-JS alternative to styled components. It claims to have the features of styled components, but takes a more “CSS” like approach as its main guideline. This is attractive to people that don’t like the component decoration approach of styled components and prefer the “good old” CSS way of writing.
My Client chose Emotion exactly for this reason.
What is theming?
I already did theming before, so the idea was clear to me, but the fashion that it’s done is dependent on the technology it uses. Let’s go over what theming is:
Theming is the ability to have a “theme”, which basically is a collection of constants. Usually, it holds things like hex colors, padding sizes, typography (font sizes, weights, font family names), etc…
Those constants are used throughout the system rather than writing the same thing over and over again. It creates a single point of change throughout the system, easily experimenting and changing them on the go.
The main advantage of using a theme as part of a css-in-js library, is that you can swap themes. From light mode to dark mode, and even color blind mode, or themes that have a larger font for people with vision impairments.
What is the basic syntax of Emotion?
So now, knowing the direction I’m heading and the technology I’m using, I needed to get my hands dirty. So I learned how to write the two main parts that usually a styling technology needs- the styling itself and its integration with the code. Let’s go through the basics:
Emotion uses JSX Pragma.
There are several ways to use it, but this is the primary one (and the one I used):
First I wrote the style:
// style.js
/** @jsx jsx */
import { css, jsx } from '@emotion/react'const style = css`
width: 100%;
height: 100%;
div {
background-color: deepskyblue;
}
`;
Then I used it via the css property in the code:
// MyComponent.jsx
/** @jsx jsx */
import { jsx } from '@emotion/react';
import style from './style';const MyComponent = props => (
<div css={style}>
<div> I'm deep sky blue</div>
</div>
);
Now, with these basics in my toolbox, I was ready to take on the design system, without the theming for now. Unfortunately, I stumbled across the raw part of Emotion, which is its utilization of pragma…
So when did it get emotional? (Pun intended)
Emotion is mostly production ready and it has all the abilities of styled components, including their version of styled components.
Now, that sounds easy to manage, doesn’t it? NOT.
There are, unfortunately, a few quirks:
Their pragma handling is very raw. I found myself writing this like in every file that used the Emotion css prop:
/** @jsx jsx */
import { jsx } from '@emotion/react';
That in combination with React 17 and Create React App 4 caused a runtime problem:
“SyntaxError: pragma and pragmaFrag cannot be set when runtime is automatic.”
Which, after some research, forced me to use this:
/** @jsxImportSource @emotion/react */
import React from 'react';
This is explained here:
“If you are using a zero-config tool with automatic detection of which runtime (classic vs. automatic) should be used and you are already using a React version that has the new JSX runtimes (hence
runtime: 'automatic'
being configured automatically for you) such as Create React App 4 then/** @jsx jsx */
pragma might not work and you should use/** @jsxImportSource @emotion/react */
instead.”
They also suggest a babel preset alternative, which didn’t solve the problem:
{
"presets": [
[
"@babel/preset-react",
{ "runtime": "automatic", "importSource": "@emotion/react" }
]
],
"plugins": ["@emotion/babel-plugin"]
}
I must admit I struggled with it quite a lot, wasting a lot of time.
Another problem was passing props. Unlike the syntax in styled components that goes like this:
const Button = styled.button`
background: ${props => props.primary ? "blue" : "white"};
`;
There are no changes needed in the component itself.
Emotion offers a much poorer implementation:
const style = props => css`
background: ${props => props.primary ? "blue" : "white"};
`;
And then pass the props like this:
const MyComponent = props => (
<div css={style(props)} />
);
So I had to make do with both restrictions, passing the props with a function and using the notorious jsxImportSource comment to complete the task. Now, all that remained is to figure out how to do the theming part.
How did the theming go?
Surprisingly, unlike the problems I just described, the theming went perfectly.
I wrote the theme:
const theme = {
colors: {
primary: blue,
success: green,
}
}
The I wrapped my application with a theme provider:
/** @jsxImportSource @emotion/react */
import React from 'react';
import { ThemeProvider } from '@emotion/react';
import theme from './theme';const ThemedApp = props => (
<ThemeProvider theme={theme} >
<App/>
</ThemeProvider>
);
and then I could use it with any component down the tree like this:
import ...const buttonStyles = theme => css`
background-color: ${theme.primary};
`;const Button = props => {
<button css={buttonStyles}>Press Me!</button>
};
Notice there’s no hook or HOC that needs to be written here manually (although Emotion offers them as well).
Now I knew all I needed to get this show running and I wrote the design system with theming to completion.
What’s the final conclusion?
My final take on Emotion is that it still has a few gaps to complete before it truly can compete with Styled Components on the same level.
Nevertheless, if you feel more aligned with the css traditional way of writing (rather than the styled components way), you are willing to struggle a bit with the pragma and you don’t mind the props handling — then Emotion is production ready for you and is a viable alternative.
It also offers the usual perks like nesting, composition, and I also used Global styles.
I’m quite certain that in less than a year from now, Emotion could stand as the alternative it wants to be.