Art by https://twitter.com/ChrisMCarrasco

emotion

The Next Generation of CSS-in-JS

Update: This was written for the original version of emotion. Since this article was published we’ve increased performance and removed the babel plugin requirement along with a host of other changes. For an up to date look at emotion check out my article on version 8.

emotion is a high performance, lightweight css-in-js library. The core idea comes from Sunil Pai’s glam library and its philosophy is laid out here. The basic idea is simple. You shouldn’t have to sacrifice runtime performance for good developer experience when writing CSS. emotion minimizes the runtime cost of css-in-js dramatically by parsing your styles with babel and PostCSS. The core runtime is 2.3kb and with React support, 4kb. Total.

First Look

The api will be familiar if you know styled-components. We’ve also taken inspiration from glamorous and css-modules.

const imageBase = css`

width: 32px;

height: 32px;

border-radius: 50%;

` const Avatar = styled.img`

composes: ${imageBase};

border: 2px solid ${p => p.theme.borderColor}



@media(min-width: 420px) {

width: 96px;

height: 96px;

}

`

Benchmarks

This benchmark pushes the libraries by changing a dynamic value in the style block very quickly.

vs. styled-components — emotion is over 25x faster when using highly dynamic prop values at a third of the size.

styled-components warns against using this pattern and instead recommends using inline styles for highly dynamic values. emotion has no such limitations.

vs. glamorous — emotion is up to 2.5x faster on rerender at half the size.

css

import { css } from 'emotion'

css is the ❤️ of emotion. Most of the api, including styled , are just wrappers around css .

const imageBase = css`

width: 32px;

height: 32px;

border-radius: 50%;

`

css is a tagged template literal that accepts a standard css text with a few additional features such as nesting, pseudo selectors, and media queries.

css returns a string class name that can be used on any element. For our imageBase style block above it would be something like css-imageBase-12345 .

To aid with composition css accepts a special property in the style block called composes .

With composes we can use our imageBase from above to quickly create avatar styles.

const avatarStyle = css`

composes: ${imageBase};

border: 1px solid #7519E5

`

Internally emotion will append these css classes to the generated one. avatarStyle from above would generate a class name like css-imageBase-12345 css-avatarStyle-12345 This allows for some really powerful composition that really shines with styled .

css also accepts object styles 😏

const imageStyles = css({

width: 96,

height: 96

})

object styles are not auto-prefixed.

styled

import styled from 'emotion/react'

styled is a thin wrapper around css and the supports the same style text and expressions as it does.

styled is modeled almost exactly like styled-components styled function.

const Avatar = styled.img`

width: 32px;

height: 32px;

border-radius: 50%;

`

styled also works as a function call. The first argument can be any html tag or React component that accepts a className prop.

const BigAvatar = styled(Avatar)`

width: 96px;

height: 96px;

`

You can also use another styled component as a selector.

const Heading = styled.img`

font-family: serif;

` const Header = styled.header`

display: flex;

${Heading} {

font-size: 48px;

align-self: flex-end;

}

`

Composes works too

const imageBase = css`

width: 32px;

height: 32px;

border-radius: 50%;

` const Avatar = styled.img`

composes: ${imageBase};



@media(min-width: 420px) {

width: 96px;

height: 96px;

}

`

In styled , the value of a composes interpolation can be a function. It is called with the current props on render. It must return an className or object style.

const types = {

success: css`

background: green;

`,

error: css`

background: red;

`,

} const Alert = styled.div`

composes: ${props => types[props.type]};

padding: 10px;

`

Remember css just returns a className so this works.

The true power of composes shines through when used with something like styled-system by Brent Jackson.

Theming

import { ThemeProvider } from 'emotion/react'

Theming is provided by the theming library. The api is laid out in detail here. It is both based on styled-components theming and heavily tested which made it a no brainer.

Whenever you provide a theme to ThemeProvider any styled component has access to those styles via props.theme . It does not matter how nested your component is inside ThemeProvider , you still have access to props.theme .