CSS and Styling

Styling is a topic of its own in React. React does not offer its own in-house solution to make styling easier, however the introduction of CSS-in-JS has shaken up the scene a little bit. Adopted widely and loved by some but hotly debated by others. With CSS-in-JS, the styling of components also moves into JavaScript to not break with the paradigm of component-based development. But let's start with the basics and explore the topic bit by bit.

Styling with the style attribute

The simplest way to style components in React is using the style attribute on regular HTML elements. It differs from regular HTML though. React components expect an object in the form of property: value. The property itself needs to be declared in JavaScript (not its regular CSS counterpart) form meaning zIndex instead of z-index, backgroundColor instead of background-color or marginTop instead of margin-top. If the values accept declarations in pixels, it is optional to explicitly define px as the corresponding unit:

<div style={{ border: '1px solid #ccc', marginBottom: 10 }}>
  A div with a grey border and a 10 pixel margin at the bottom
</div>

Values that are not used with a unit (for example z-index, flex or fontWeight) are not affected by this and can be used just like we are used to. React does not add px to these.

By using an object instead of a string, React keeps a consistent approach to dealing with styles. The regular style property of DOM elements document.getElementById('root').style is also an object and also ensures that we secure these by preventing XSS.

While using inline styling is not exactly recommended, it can be useful at times if the styling of an element depends on particular values in state.

Using CSS classes in JSX

It is much cleaner and nicer to use real CSS classes in JSX, just like we do in regular HTML with the difference being that we declare classes by using className instead of class:

<div className="item">...</div>

React renders normal syntax with our HTML equivalent:

<div class="item">...</div>

It is also quite common to concatenate values in the className prop dynamically:

render() {
  let className = 'item';
  if (this.state.selectedItem === this.props.itemId) {
    className += ' item--selected';
  }
  return (
    <div className={className}>
      ...
    </div>
  );
}

In this example the value for className is item in each case. If the selected item is the current item, it also gets the class item item--selected.

The package classnames has become the de-facto standard to define classes based on a condition. It can be installed via the CLI:

npm install classnames
yarn add classnames

Now, we only need to import the package in those components where we want to use it:

import classNames from 'classnames';

This way, we are importing the function and give it the name of classNames. The function expects an arbitrary amount of parameters which can be a string or an object in the form of { class: true | false }. It works similar to what you might already expect: if the value in the condition is true, classNames employs a class with the same property name:

render() {
  return (
    <div className={classNames('item', {
      'item--selected': this.state.selectedId === this.props.itemId
    })}>
      ...
    </div>
  );
}

Objects can also be used to work with multiple properties. Multiple classes can be set if the condition is true:

render() {
  return (
    <div className={classNames({
      'item': true,
      'item--selected': this.state.selectedId === this.props.itemId
    })}>
      ...
    </div>
  );
}

If regular CSS classes are used, we should also ensure that the corresponding stylesheet is also linked in the HTML document. React does not usually take care of this on its own.

Modular CSS with CSS Modules

CSS Modules are some sort of predecessor to CSS-in-JS and combine a number of properties from CSS and JavaScript modules. As the name already suggests, the CSS is found in their own importable modules, which however contain pure CSS and are tied to one particular component. If we were to develop a component to display a profile picture in a file called ProfileImage.js, the CSS Modules approach often introduces another file with the name ProfileImage.module.css.When these CSS modules are imported, a cryptic classname is generated to ensure that the classname is only used in the single component. This aims to prevent that classnames are accidentally used by two different components.

CSS Modules are only a concept first of all, and do not dictate a particular approach or implementation. The implementation is achieved by tools such as css-loader for Webpack.

It's not actually necessary to enforce the file ending of .module.css (just regular .css would also be okay) but it has become convention in many well-known tools. Create React App also supports this convention out of the box without the need for further configuration by using the previously mentioned css-loader in the background (implemented in Webpack).

In these CSS files, regular and standardized CSS can be found. It can then be imported into a JavaScript module with ease:

import css from './ProfileImage.module.css';

We've just entered new territory: the variable css now containts an object with properties that relate to the classnames in our CSS modules. If there is a CSS class in the CSS file that has the name image, we can now access the property image on the CSS object. The resulting value in the rendered markup is a unique string such as ProfileImage_image_2cvf73.

We can pass these generated classnames to our components like this:

<img src={props.imageUrl} className={css.image} />

This code would result in the following rendered markup:

<img src="..." className="ProfileImage_image_2cvf73" />

If we used the class image in another component and also imported the css file with an image class, we wouldn not run into conflict as we would usually do in regular CSS. The generated classname would be different.

Everything that is allowed in regular CSS is also allowed in CSS Modules. The Cascade will remain intact:

.imageWrapper img {
  border: 1px solid red;
}

.imageWrapper:hover img {
  border-color: green;
}

This CSS snippet would be used in the following JSX structure:

const ProfileImage = () => {
  return (
    <div className={css.imageWrapper}>
      <img src="..." />
    </div>
  );
};

CSS Modules aim to avoid conflicts in very isolated components while still allowing us to write regular CSS in CSS files without any JavaScript knowledge. They are a great solution for teams in which development is more separated into JavaScript and CSS and in which there are experts for both.

CSS-in-JS - Moving styles into JavaScript

I've already mentioned in the introduction that CSS-in-JS is a bit of a hotly debated topic. Opponents say that users of CSS-in-JS do simply not understand the cascade to write scalable CSS. Proponents on the other hand explain that the cascade is not the main reason for choosing CSS-in-JS but argue that a safer way is needed to write highly isolated components. I'm a bit more diplomatic myself and think that there's room and reasons for both. CSS-in-JS certainly has reason to exist. But what even is CSS-in-JS?

The CSS-in-JS approach mandates that the styles which are commonly found in CSS files are now moved into JavaScript. As was already the case in CSS modules, the primary goal is to create highly isolated components which are free of conflict making them easy to reuse throughout the application. However, as opposed to CSS modules, the styling in CSS-in-JS happens entirely in JavaScript. The syntax is very similar and it mostly feels like you're writing regular CSS.

CSS-in-JS is the umbrella term for the concept whereas the actual implementation is achieved by a number of different libraries. If you are looking for a great overview, I suggest you checkout out http://michelebertoli.github.io/css-in-js/ — this site covers over 60 different libraries that can help you to implement your own CSS-in-JS solution.

In the remaining parts of the chapter, I want to focus on styled components which is the most popular library with around 23,000 stars on GitHub. In order to use it, we need to first install it via the command line:

npm install styled-components
yarn add styled-components

Once installed, we are ready to import it and write our first styled component. Using the styled function, we define the HTML element that we want to style. Template literal syntax from ES2015 is used to define the CSS for our styled component.

In order to create a button which is black and yellow, we would need to write the following:

import React from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';

const Button = styled.button`
  background: yellow;
  border: 2px solid black;
  color: black;
  padding: 8px;
`;

const App = () => {
  return (
    <div>
      <h1>An example of a styled component</h1>
      <Button>A black and yellow button without any function</Button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));

By using the notation const Button = styled.button styled components creates a new Button component and equips it with the CSS properties that we have defined within the template literals. Inside of the template literals, regular CSS can be used.

If we want to use pseudo selectors or elements, we can use these without their previous selector. For a :hover status, we can define the following:

const Button = styled.button`
  background: yellow;
  border: 2px solid black;
  color: black;
  padding: 8px;

  :hover {
    background: gold;
  }
`;

Props can also be accessed in styled components. By calling a function within the template string, all props of the element can be passed as the first parameter:

const Button = styled.button`
  background: yellow;
  border: 2px solid black;
  color: black;
  cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')};
  padding: 8px;

  :hover {
    background: gold;
  }
`;

The cursor icon would change into a not-allowed symbol if the button had a disabled property. Moreover, styled components offers support for theming, server-side rendering, css animations and much more. For those of you who would like an in-depth explanation and overview, I suggest you check out the thorough documentation.

The main advantages of CSS-in-JS and styled components in particular are well summarized in the documentation:

  • Critical CSS (meaning the CSS which is relevant for the current page) is automatically generated as styled components knows which components are used on each page and which styles they need

  • By automatically generating classnames, the risk of conflicts is reduced to a minimum

  • As all CSS is tied to a component, it is easy to spot redundant CSS. If a styled component is no longer used in the application, the CSS is no longer needed either and the component can be safely deleted.

  • Component logic and component styling (CSS-in-JS) are found in the same spot, sometimes even in the exact same file. Developers do not need to spend a long amount of time anymore to find where exactly a styling change needs to take place.

  • Styled components automatically generates CSS that contains vendor prefixes for all browsers.

Styled components is only one of many CSS-in-JS solutions. While it is probably the most well-known and most widely adopted, there are a number of great alternatives which can help you to find a solution which fits your project's specific needs. Other great and well-known alternatives are:

Last updated