loading...

Creating a common component library with CSS-in-JS (Styled Components, JSS, Emotion)

akirautio profile image Aki Rautio Updated on ・5 min read

CSS-in-JS is a wide term that covers a lot of different practices that enable us to write styles straight to React components. Combining style to functionality gives a lot more flexibility to write state-dependent styles and this also fixes the issue of having the only one namespace.

In a most simplistic form, this can be done by writing styles straight to HTML tags through a style property but for any bigger application, this brings some downsides. Even though the styles are written multiple times, inline styling will be kept in DOM under each component which increases the size of the DOM very fast and decreases the performance. Luckily there are many ways to solve this issue.

const Component = (props) => (
  <div style={{
    backgroundColor: 'black',
    border: '1px solid black'
    }}
  {...props}  
  >
    {children}
  </div>
)

Tools

CSS-in-JS packages focus on enhancing a developer experience by bringing better extendability and theming, and performance by compiling styles back to CSS classes. Due to a fastly increased interest in this technique, a lot of packages have been created to include styling to React components. One of the most popular actively developed packages are:

Besides these, there are a lot of different options so you are not limited to these five choices. Though I wouldn't too much look for the performance results since all of the biggest packages are usually fast enough. Most of the tools use randomly generated class names to avoid multiplying styles in DOM.

In general, these tools can be split into two categories; ones which help out creating class names easier (like JSS and Linaria) and ones that abstract the class names (like Styled Components or Emotion).

Setup

Unlike CSS and SASS, CSS-in-JS doesn't need a complicated setup because it doesn't require compiling. The minimum setup is to install the package and import it when needed.

Example with JSS

JSS does styling by generating a random class name based on given configuration and this class name which then can be used in the component. It automatically adds CSS under head tag so we don't have to worry about it.

import React from 'react'
import { createUseStyles, useTheme } from 'react-jss'

const useStyles = createUseStyles({
  myButton: {
    padding: theme.big ? 8 : 4,
    '& span': {
      fontWeight: 'bold',
      color: 'white'
    }
  }
})

const JSSButton = ({ children, big, ...props }) => {
  const classes = useStyles({ big, ...props })
  return (
    <button className={classes.myButton}>
      <span>{children}</span>
    </button>
  )
}

Example with Styled components

Styled components handle both component and styles together inside the styled function and it enables us to use template literals when creating styles, which is pretty close to normal CSS.

const StyledButton = styled(({ children, ...props }) => (
  <button {...props}>
    <span>{children}</span>
  </button>
))`
  padding: ${({ big }) => (big ? '8px' : '4px')};
  & span {
    font-weight: bold;
    color: white;
  }
`

Extending

One of the greatest features on CSS-in-JS is extendability. This makes it very easy to create common components which can be later extended to more detailed. For example if we would like to extend the StyledButton to have extra borders, it would look like this:

const BorderedStyledButton = styled(StyledButton)`
  border: 1px solid black;
`

Extendability brings the power to modify the base elements as much as needed but it also poses risk to have an inconsistent design due to many extended features. That's why it should be used only when we add extra feature like rotatability to Icon or hover effect to specific places.

Theming

Most of the CSS-in-JS packages also provide theming support. In theming we separate common variables like colors to the shared location. When the color is used in component, we refer it by the variable name instead of writing the color code.

For example in JSS and Styled Components theming works he same way as Context. First the application is wrapped with ThemeProvider and thne all theme variables are available to be used inside the theming through provider hook or props.

JSS example

import { ThemeProvider, createUseStyles, useTheme } from 'react-jss' 

const useStyles = createUseStyles(theme => ({
  myButton: {
    background: theme.colorPrimary
  }
}))


const JSSButton = ({ children, big, ...props }) => {
  const theme = useTheme() // This provides theme variables
  const classes = useStyles({ big, ...props, theme })
  return (
    <button className={classes.myButton}>
      <span>{children}</span>
    </button>
  )
}

const App () => (
<ThemeProvider theme={{ colorPrimary: 'green' }}>
  <JSSButton>
</ThemeProvider>
)

Styled components example

import styled, { ThemeProvider } from 'styled-components'


const StyledButton = styled(({ children, ...props }) => (
  <button {...props}>
    <span>{children}</span>
  </button>
))`
  background: ${({ theme }) => theme.colorPrimary}; // Theme is provided automatically
`

const App () => (
<ThemeProvider theme={{ colorPrimary: 'green' }}>
  <StyledButton>
</ThemeProvider>
)

My experience

I have been using CSS-in-JS packages for some of my projects and my personal preference has been using packages that hide the className assignment. For bigger projects, I have been relying on Styled Components with styled-system while CXS has been used in smaller ones.

In my opinion, CSS-in-JS brings value by abstracting the styling inside component which functionality and design in one place. This unifies the way I work with the components and it makes style properties (like big in a button example) to be just one parameter in a component.

Using CSS-in-JS to create a common component library makes a lot of sense since the key design can be capsulated inside the component and only needed properties can be exposed. This also helps developers who use common components to get an idea what they can change.

CSS-in-JS also makes extending components much easier and more comprehensible and this pushes to create a simple base component that can be enhanced on a need-based basis. Having a set of common extensible components makes development much faster as you never have to start from the beginning.

The downside is that this is a very developer-friendly way to handle styling so a person without knowledge of javascript may not be able to do much. This matters a lot when the project involves people with different skillsets.

Summary

The biggest advantages of CSS-in-JS are a lack of common namespace which ensures that styles will never collide and easy extendability. On the other hand, the enchancements makes the styles harder to read for the people who haven't been used to the javascript.

CSS-in-JS can be written without any packages, but the biggest value comes when it's combined with a package that handles the conversion from the style to the class name. The biggest difference between packages is whether they abstract only the class name generation or the whole component generation.

Repository css-in-js has an example how to use this in a project.

This is a third post from the series Speed up development by creating a common Component library. The later posts will cover up the other options to build the common component library and how to document the library.

Discussion

pic
Editor guide