Max RozenMax Rozen
TwitterArticlesNewsletter

A Guide to Styling your React App

If you’re new to React, you might be wondering why there are so many different tutorials that teach different ways to style your React app. The truth is, we’re all still figuring out the best way to do things.

Styles in React were more-or-less worked out in this order:

  1. Global CSS
  2. CSS Modules
  3. CSS in JS (styled-components, Emotion, etc)

    • Utility-first CSS
    • Styled System
  4. Statically extracted CSS in JS

These days, I recommend starting with CSS in JS. If you’d like to know why, read on.

Quick note

When I say styling, I mean writing your CSS styles more-or-less from scratch. If you’re looking for pre-built components, I wrote a guide to commonly used React component libraries.

Global CSS

Global CSS is likely the way you’re used to styling webpages. You have a giant styles.css file, and try to write BEM or SMACSS names for all of your classes. Alternatively, you have a ton of tiny files, and don’t always know where each class lives.

We as frontend developers quickly realised that global CSS doesn’t really scale. The more teams you have editing a single file, the more likely you are to have CSS that doesn’t do anything (people become too afraid to delete anything in case it breaks).

If you still want to use Global CSS in your React app, all you need to do is import the CSS file at the top level of your React app (assuming you’ve configured webpack to do so, or are using create-react-app).

//App.js
import './styles.css';
import React from 'react';

const App = () => {
  return <div className="some-class">some other stuff in here</div>;
};

CSS Modules

CSS Modules look a lot like Global CSS, in the sense that you’re importing a CSS file into your React component, but under the hood it’s quite different.

A lot of the problems we used to have with Global CSS are gone with CSS Modules.

Your CSS looks like this:

/* style.css */
.makeItGreen {
  color: green;
}

and your React Component looks like this:

import React from 'react';
import styles from './style.css';

const MyComponent = () => {
  return <div className={styles.makeItGreen}>Green!</div>;
};

The key difference here is that only files that import style.css will be able to access the class names that it defines, and the class names that get generated during the build process will be unique.

No more conflicts, no more “too afraid to delete things in case it breaks”, just locally scoped CSS. You can also set-up SCSS/LESS support, if you need it.

The really cool thing about this is that you can start to play around with JavaScript to change a component’s styles.

import React from 'react';
import styles from './style.css';

const MyComponent = (props) => {
  const myStyle = props.color === 'RED' ? styles.makeItRed : styles.makeItGreen;
  return <div className={myStyle}>{props.color}!</div>;
};

Although, that starts to get a bit messy if you’re using several props to change the style and behaviour of your components. What if your styles could just be components?

CSS in JS

That’s where CSS in JS comes in.

Libraries like styled-components and Emotion make it possible to wrap components (including divs, spans, <p> tags, <a> tags) with styles, and use them as React components.

The best part is, you can use all of the standard CSS features you’re used to, such as media queries, and :hover and :focus selectors.

Our example from above now becomes:

import React from 'react';
import styled from '@emotion/styled';
// OR import styled from 'styled-components'

const StyledGreenThing = styled.div`
  color: ${(props) => (props.color === 'RED' ? 'red' : 'green')};
`;

const MyComponent = (props) => {
  return (
    <StyledGreenThing color={props.color}>{props.color}!</StyledGreenThing>
  );
};

As of 2020, Emotion and styled-components are evenly matched performance-wise. Contributors to styled-components worked hard to bring their performance up to Emotion’s level, so deciding which one to use isn’t as much of a big deal any more (even if people argue endlessly about them as though they’re sports teams).

Emotion does provide some extra options for styling, such as the css prop, while styled-components tries to keep a single, standard way of doing things via the styled API.

Utility-first CSS

A guide to styling React apps wouldn’t be complete without mentioning utility-first CSS frameworks such as Tailwind. You don’t actually need React to use utility-first CSS, but in my opinion, it makes for a better developer experience when you add React and CSS in JS.

In short, Tailwind lets you style your components one class at a time. Here’s what it looks like:

<div className="md:flex bg-white rounded-lg p-6">
  <img
    className="h-16 w-16 md:h-24 md:w-24 rounded-full mx-auto md:mx-0 md:mr-6"
    src="avatar.jpg"
  />
  <div className="text-center md:text-left">
    <h2 className="text-lg">Erin Lindford</h2>
    <div className="text-purple-500">Product Engineer</div>
    <div className="text-gray-600">erinlindford@example.com</div>
    <div className="text-gray-600">(555) 765-4321</div>
  </div>
</div>

Which creates a component that looks like this: Tailwind CSS Example Component

You might be thinking that it’s not a particularly re-usable solution, however it’s possible to use Tailwind class names with your favourite CSS in JS library using twin.

You can then have styled Tailwind components:

import tw, { styled } from 'twin.macro';

const Input = styled.input`
  color: purple;
  ${tw`border rounded`}
`;

export const MyStyledInput = () => {
  return <Input />;
};

Styled System

Styled System takes the styled API supplied by styled-components or Emotion, and adds utilities as props, rather than class names.

The Styled System approach is particularly powerful when it comes to theming/white labelling, as changing the entire appearance of your app can be done by replacing the theme.js file you provide.

Your components end up looking like this:

import styled from '@emotion/styled';
import { typography, space, color } from 'styled-system';

const Box = styled('div')(typography, space, color);

const UsedBox = () => {
  return (
    <Box
      fontSize={4}
      fontWeight="bold"
      p={3}
      mb={[4, 5]}
      color="white"
      bg="primary"
    >
      Hello
    </Box>
  );
};

Statically extracted CSS in JS

The trouble with CSS in JS is that it takes JavaScript to load your CSS. This slows things down big time, so people started looking for ways to extract the CSS from CSS-in-JS during build time.

There are a few libraries that can do this:

Compiled and linaria let you use the styled API that you know and love, while giving you the performance benefit of not having CSS in your bundle.

Do you struggle to keep up with best practices in React?

I send a single email weekly with an article like this one to help improve the quality of your React apps. Lots of developers like them, and I'd love to hear what you think as well. You can always unsubscribe.

    Join 43 React developers who signed up last week!

    rssTwitterGitHub

    © 2020 Max Rozen. All rights reserved.