You probably shouldn't use @emotion/core
Tags: emotion, ssr, react, cssinjs
Published: 2020-06-07 09:37:38

---
title: You probably shouldn't use @emotion/core
date: 1591496878356
published: true
author: Colin McDonnell
author_handle: vriad
tags: Emotion, ssr, React,cssinjs
author_image: https://vriad.com/colin_square_small.jpg
cover_image: https://vriad.com/emotion_core_bad.png
thumb_image: https://vriad.com/emotion_core_bad_small.png
canonical_url: https://vriad.com/essays/emotion-core-vs-vanilla-emotion
---

<!-- twitter -->

> A maintainer of Emotion published a very thoughtful response to a version of this post! I encourage you to check that out [here](https://github.com/emotion-js/emotion/issues/1883#issuecomment-637827074). The blog post below has modified to address some of his points.

Emotion is my favorite CSS-in-JS library.

It's easy to define style classes — both inline or in separate files. You can powerfully compose classes in powerful ways with the `cx` utility (the Emotion equivalent of Jed Watson's [classnames](https://github.com/JedWatson/classnames)). You sprinkle in your styles using the standard `className` attribute. There's no need to modify your markup/JSX — as it should be! You only need to install a single module (`yarn add emotion`). And there's no complicated Babel plugin or config file to set up.

```tsx
import { css, cs } from 'emotion';

const redBorder = css({ border: '1px solid red' });
const blueText = css({ color: 'blue' });

const MyComp = () => {
  return <div className={cx(redBorder, blueText)}>hello</div>;
};
```

I'm currently building a Tailwind-style CSS-in-JS utility styling library ([follow me](https://dev.to/vriad) to hear when it's ready!). Emotion provides the perfect layer of abstraction to build upon.

### The "but"

But unfortunately everything I just said only applies to the "vanilla" `emotion` module ([docs](https://emotion.sh/docs/emotion)), _not_ the confusingly-named `@emotion/core` module.

`@emotion/core` is the React-centric Emotion wrapper that gives you some extra goodies, specifically server-side rendering and theming. It's also the official recommendation of the Emotion project for any React project.

![Emotion's recommending emotion core](https://vriad.com/emotion_recommendation.png)

So why exactly is Emotion recommending this for React devs?

## Minimal upsides

The main three advertised benefits of using `@emotion/core` are server-side rendering (SSR), theming, and customizability. Let's dig into those.

### Out-of-the-box SSR

There's no denying this is a remarkable technical achievement. Getting SSR to "just work" with both Next.js, Gatsby, and the classic `ReactDOMServer.renderToString` approach is very impressive. I'd be lying if I claimed to understand the complexities involved.

I don't have data on this, but — in my experience — SSR isn't a consideration for a hefty majority of React projects. If you started a project/website in the last 7 years where SEO/SEO/pageload speed/bundle size was an important design consideration, you probably didn't choose React. Website builders, static site generators, and HTML templating still dominate that arena. Take it from someone who got [torn apart on HN](https://news.ycombinator.com/item?id=23309002) for advocating the use of React/Next.js for personal developer websites 😘

<!-- 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 telling them how insanely easy it is to set up SSR themselves with vanilla `emotion`. -->

For the people who do need SSR, the guidance is a little slim.

#### Next.js

There's no explicit documentation from Next.js on how to set up SSR with vanilla `emotion`. Next.js provides a sample project [here](https://github.com/vercel/next.js/tree/master/examples/with-emotion). Notably, this project a) has a very uninformative 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 the internet's first comprehensive guide to setting up SSR with vanilla Emotion and Next.js:

1. `yarn add emotion-server`
2. create `_document.tsx` in your `pages` directory and copy [this gist](https://gist.github.com/vriad/c40519a6a050a99091862319151377ec) into it
3. ok ur done

#### Gatsby

For completeness, here's some instructions for Gatsby users, too.

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

If you're using @emotion/core to avoid the complexities of SSR configuration, you may want to reconsider.

### Theming

In the era of React Context and Hooks, there's no reason for libraries to use prop or high-order components to handle theming. Emotion provides a useTheme hook, but it still requires adding an additional library (`emotion-theming`).

This isn't a controversial claim; the next version of Emotion [will explicitly recommend](https://github.com/emotion-js/emotion/pull/973/files) using a Context/Hook based solution, so I won't belabor this point.

<!-- Touting this as a feature is a little silly. 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.  -->

<!-- Of course, if you theme doesn't change dynamically (and is it really a theme if it does?) just define your theme properties as variables and use import them into your components as needed. This is part of the appeal of CSS-in-JS — it lets you treat your styles just like any other code. If you're using TypeScript here's some code to get you started: -->

Even Context/Hooks might be overkill for many projects. Just define your theme values as variables and import them into components as needed. If you're using TypeScript here's some code to get you started:

```tsx
// theme.ts
export const primaryColor = "blue";
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:

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

### Customization

> Edit: This section was added after the fact to address a concern raised by the maintainer of Emotion in [his response](https://github.com/emotion-js/emotion/issues/1883#issuecomment-637827074) to an earlier version of this post.

@emotion/core provides a `CacheProvider` component that lets you customize low-level aspects of it's behavior. This customization isn't possible with vanilla emotion. I'll let the maintainer of Emotion explain it:

> Because [@emotion/core] doesn't give you string class names back, but rather just opaque objects, the rule insertion is lazy. This might not be as interesting on its own, but it allows us to customize injection through a <CacheProvider>. Use cases for this are not as common - but they exist and once you ever come across one then you might be very thankful for the path we've chosen...Main things that can be customized:
>
> - prefixing rules
> - parser plugins
> - container element (very important for rendering into iframes)
> - nonce (very important for CSP)
>
> — Andarist ([Github](https://github.com/Andarist))

If you absolutely need this degree of customizability, then @emotion/core is probably right for you.

For everyone else, let's look at the downsides.

## Serious downsides

### The `css` prop

Emotion [recommends using](https://emotion.sh/docs/css-prop) their non-standard `css` prop to style your components, instead of React's built-in `className`. This causes me immeasurable emotional pain.

<!-- For starters, supporting a non-native prop requires Emotion to entirely hijack React's JSX parsing, which seems messy and overkill. -->

This approach destroys the portability of your React components. Your components are now unusable in any codebase that isn't configured to use `@emotion/core`.

The portability and encapsulation of React components is one of the most powerful and wondrous achievements in web development in the last last decade. Don't give that up without a good reason!

### Installation woes

Unfortunately to get that non-native `css` prop to work, Emotion core entirely replaces your project's JSX parser. It substitutes the built-in `React.createElement` function with Emotion's custom `jsx` function.

![Replacing React's createElement function](https://vriad.com/jsx_replacement.png)

There are a couple ways to set this up.

Option #1: install the `@emotion/babel-preset-css-prop` Babel plugin and add it to your `.babelrc`. If you're using Create React App, this isn't impossible. If you're using TypeScript, you probably don't have a `.babelrc` in your project.

If you're in one of those buckets, there's option #2: copy these two lines at the top of every React component you want to style with Emotion:

```tsx
/** @jsx jsx */

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

<!-- This is a pragma statement that tells Babel to use Emotion's `jsx` function to create React elements instead of React's built-in `React.createElement` function. -->

<!-- `@emotion/core` in every component file (just like you need `import React from 'react'` in every file containing JSX). -->

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](https://github.com/emotion-js/emotion/issues/1046) if you want to see dozens of TypeScript users being sad about this.

![Sad Typescripters with linter warnings](https://vriad.com/sad_emotion_small.png)

### Lack of composability

Perhaps the most damning issue with @emotion/core is that it makes the simple things harder. If you want to define a new class or use `cx`, you have to wrap your component with the `ClassNames` render prop. But with @emotion/core, these basic functions — found in nearly all CSS-in-JS libraries — requires you to modify your markup. In my humble opinion, requiring markup modifications is a cardinal sin for a styling library.

Here's the example from the top of this post, reimplemented with @emotion/core:

```tsx
import { ClassNames } from '@emotion/core';

const MyComp = () => {
  return (
    <ClassNames>
      {({ css, cx }) => {
        const redBorder = css({ border: '1px solid red' });
        const blueText = css({ color: 'blue' });

        return <div className={cs(redBorder, blueText)}>hello</div>;
      }}
    </ClassNames>
  );
};
```

## Wrapping up

I understand how this happened. Vanilla emotion was undoubtedly getting flooded with GitHub issues by frustrated developers hitting against subtle limitations of its design. @emotion/core fixes these issues. But because @emotion/core is now the officially recommended approach for _all_ React projects (the vanilla option isn't even mentioned on the Readme anymore), I suspect thousands of developers using it who would be better served by plain ol' `emotion`.

And finally: a huge thank you to the Emotion team for all their exceptional work and contributions to the open-source community.

<!-- newsletter -->