DEV Community

Cover image for CSS in JS
Matt Thorning
Matt Thorning

Posted on • Originally published at hellocode.dev on

CSS in JS

I've looked at CSS in JS libraries before but there always seemed be trade-offs which have made them appear less attractive than Sass or even plain CSS. However, I have recently been playing around with Emotion and I'm pretty impressed so I thought I would write a quick post showing some examples of what it can do.

I've been using Emotion with React, there is a React-specific package (@emotion/core) which provides some additional features but I have just been using their framework-agnostic version. There are several different ways that you can use Emotion, the examples below use the method that works best for me, check out their documentation if you're interested in what else it can do.

Basic usage

Emotion uses tagged templates to create a CSS class which is then applied to the element. Here is the basic pattern for applying styles to a React component:

import React from 'react'
import { css } from 'emotion'

export default function TestComponent(props) {
  return (
    <div
      className={css`
        background: blue;
        border: 1px solid red;
        max-width: 300px;
      `}
    >
      Hello
    </div>
  )
}

Notice that the string passed to the tagged template is actual CSS, there are no camel-cased keys, the values do not need to be wrapped in single quotes and the lines are terminated with semi-colons. At first I was disappointed because the syntax highlighting in VS Code was messed up but I added the Babel Javascript extension which formats the CSS as if it were in a .css file so now it looks nice. 👍

vs code syntax highlighting

If you look at the markup you can see that Emotion assigns a class to the elements with a random string appended to it.

divs with classes

This scopes your CSS to the element it is applied to so it won't leak to other components, making your styling component-based as opposed to document-based which fits better with the way that React apps are written. If you want to set global styles then Emotion has a Global component you can use, and there is nothing stopping you from using a global stylesheet in addition to your component-level styles.

I started off by writing my styles straight into the JSX but things began to look messy. Fortunately it's really easy to extract the rules and move them above the JSX or even into their own file:

import React from 'react'
import { css } from 'emotion'

export default function TestComponent(props) {
  const styling = css`
    background: blue;
    border: 1px solid red;
    max-width: 300px;
  `
  return (
    <div
      className={styling}
    >
      Hello
    </div>
  )
}

Just like CSS

It's possible to do nested components, pseudo-classes and media queries:

const styling = css`
  background: blue;
  border: 1px solid red;
  max-width: 300px;

  a {
    color: black;
  }
  &:hover {
    background: pink;
  }
  @media (max-width: 400px) {
    font-size: 12px;
  }
`

If you check the Emotion documentation there are some pretty clever things you can do with media queries if you have a lot of breakpoints to cater for, I won't go into it here but take a look if you're interested.

Just like JS

Because these are just strings you can do interpolation:

const baseStyle = css`
  width: 300px;
  border: 1px solid black;
`
const green = css`
  ${baseStyle}
  background: green;
  `
const blue = css`
  ${baseStyle}
  background: blue;
`
const red = css`
  ${baseStyle}
  background: red;
`
return (
  <>
    <div className={green}>Green</div>
    <div className={blue}>Blue</div>
    <div className={red}>Red</div>
  </>
)

You could take this further and use functions:

const baseStyle = css`
  width: 300px;
  border: 1px solid black;
`
function styles(color) {
  return css`
    ${baseStyle}
    background: ${color};
  `
}

return (
  <>
    <div className={styles('green')}>Green</div>
    <div className={styles('blue')}>Blue</div>
    <div className={styles('red')}>Red</div>
  </>
)

And it's not just values, any part of the string can be interpolated:

const smallScreen = '@media (max-width: 400px)'

const style = css`
  ${smallScreen} {
    font-size: 12px;
  }
`

This is great for allowing you to set up a file of constants which can be imported into all of your modules for consistency or theming.

It's also worth remembering that the css function (tagged template) returns a class so you can dynamically add styles as you would with classes:

const baseStyle = css`
  color: ${textColor};
`

function TagBlock(tagName) {
  const highlighted = tagSelected && css`
    color: ${highlightColor};
  `
  return (
    <span className={css`
      ${baseStyle}
      ${highlighted}
    `}>
      {tagName}
    </span>
  )
}

In this example the component will change the colour of the text to highlightColor when tagSelected is true.


I've only been using Emotion for a very brief time and have only scratched the surface of what it can do. Transitioning to it has been extremely easy because it offers all of the functionality of CSS but with the flexibility of JavaScript. The compositional way in which Emotion allows you to build your CSS compliments React nicely, particularly now that hooks are live. This is particularly noticeable when you reach a point where you want to extract some logic into its own component; simply cut and paste the JSX, hooks and styling out of your component and paste them into a new file.

This has just been a quick look at Emotion, I haven't tried other CSS-in-JS libraries, but I was impressed enough by my experience with it that I wanted to put together this quick post. Hopefully it's been helpful to someone!

Oldest comments (0)