Skip to content

Instantly share code, notes, and snippets.

@colinhacks
Last active October 20, 2020 17:52
Show Gist options
  • Save colinhacks/23956586223e36931718399c0caa2eef to your computer and use it in GitHub Desktop.
Save colinhacks/23956586223e36931718399c0caa2eef to your computer and use it in GitHub Desktop.

Emotion is my favorite CSS-in-JS library.

It's easy to define style classes (both inline or in separate files), compose them in powerful ways with the cx utility, sprinkle them into React components using the standard className attribute. There's no need to ever modify your markup/JSX to apply styles (as it should be!). Plus you only need to install a single module (yarn add emotion) and there's no complicated Babel plugin or config file to set up.

I'm currently building a Tailwind-style CSS-in-JS utility styling library (join my newsletter at the bottom of this page to stay updated!) and Emotion provides the perfect layer of abstraction to build upon.

Why @emotion/core is bad

Unfortunately everything I just said only applies to the vanilla emotion module (https://emotion.sh/docs/emotion), not the inexplicably-named @emotion/core module. @emotion/core is the React-focused Emotion wrapper that gives you some extra goodies, like server-side rendering and theming.

Installation woes

To install @emotion/core, you need to install the @emotion/babel-preset-css-prop Babel plugin and add it to your .babelrc. If you're using Create React App, this is simply impossible. If you're using TypeScript, you may not have seen a .babelrc file in years.

If you're in one of those buckets, there's a fallback solution! Just copy these two lines at the top of every React component you want to style with Emotion:

/** @jsx jsx */

import { jsx } from '@emotion/core';

Yes, you have to import jsx from @emotion/core even if you don't use it. If your TypeScript config or linter doesn't allow unused imports, you'll have to disable those rules to get rid of the warning. Check out this issue if you want to see hundreds of sad, frustrated TypeScript fans complain about this over the last 18 months.

The css prop

Emotion also recommend using their non-standard css prop to style your components, instead of React's built-in className. And as someone who's building a styling library on top of Emotion, I hate that this is the officially recommended approach for React projects.

To implement this custom css prop requires Emotion to entirely hijack React's JSX parsing (), which seems messy and overkill.

The usage of a non-native css prop kneecaps the portability/extensibility of your component; it's now unusable in any codebase that isn't configured to use @emotion/core. Bad!

ClassNames

If you want to stick with using the className prop or combine Emotion classes with cx (the Emotion equivalent of the classnames utility), you need to add the ClassNames component + render prop as the root of every component you want to style (docs). Now your styling library is forcing you to restructure your markup. Just say no! Also: render props?! What is this, 2018?

As far as I understand the only benefits of using @emotion/core over emotion are "out-of-the box server-side rendering" and built-in support for theming. Let's break those down.

Server side rendering

I appreciate the technical difficulty of getting SSR to work out of the box; it's a hard problem. I don't appreciate that Emotion tries to drive people towards @emotion/core instead of tellling them how insanely easy it is to set up SSR themselves with vanilla emotion.

Next.js

Emotion provides no concrete guidance on how to do this, just a link to a sample Next.js project (this one). Notably, this project a) has the internet's least informative Readme and b) is built with @emotion/core! So it's not immediately obvious that the approaches on display will even transfer to a vanilla project.

Enough buildup. Here's how to do it.

  1. yarn add emotion-server
  2. create _document.tsx in your pages directory and copy this gist into it
  3. ok ur done

Gatsby

  1. yarn add gatsby-plugin-emotion
  2. add gatsby-plugin-emotion to your plugins list in gatsby-config.js

Theming

Touting this "feature" is preposterous. The point of CSS-in-JS is to define our styles as a data structure in a high-level programming language. Your styles are code; you can use the full might of JavaScript to define/modify/generate your styles; that's the whole appeal That means you can define your theme properties as variables and import them wherever you need them.

If you're using TypeScript here's some code to get you started:

// theme.ts
export const primaryColor = "blue";
export const secondaryColor = "red";
export const serif = `"Merriweather", Times New Roman, Times, serif`;

// anydamnfile.ts
import { css } from 'emotion';
import * as theme from './theme.ts';

export const MyComponent = ()=>{
  return <p className={css({ color: theme.primaryColor, fontFamily: theme.serif })}>
}

If you want to import your theme with a useTheme hook, here's a workable implementation that took me several seconds to write.

import * as theme from './theme.ts';
export const useTheme = () => theme;

Perhaps there are lots of great reasons to use @emotion/core that I'm not seeing and aren't in the Emotion docs. If there are, yell at me on Twitter here: https://twitter.com/vriad! I've been wrong before, or so I'm told.

But in the meantime, I'll continue to be flabbergasted that the Emotion team decided to put all their eggs in a basket that, imho, is strictly worse than their excellent original solution. I'll also be incredibly thankful and indebted to them for building the true "core" emotion package, which is a glorious achievement and a huge win for developers!

Say to no styling libraries that require Babel plugins, JSX hijacking, markup modification, and Babel configuration to work. There are better ways!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment