Styling in React: From External CSS to Styled Components

Chris Laughlin
Chris Laughlin
Share

While many aspects of building applications with React have been standardized to some degree, styling is one area where there are still a lot of competing options. Each has its pros and cons, and there’s no clear best choice.

In this article, I’ll provide a condensed overview of the progression in web application styling with regards to React components. I’ll also give a brief introduction to styled-components.

A cosmetics kit with various 'styled-components'

Evolution of Styling in JavaScript

The initial release of CSS was in 1996, and not much has changed since. In its third major release, and with a fourth on the way, it has continued to grow by adding new features and has maintained its reputation as a fundamental web technology. CSS will always be the gold standard for styling web components, but how it’s used is changing every day.

From the days when we could build a website from sliced up images to the times when custom, hand-rolled CSS could reflect the same as an image, the evolution of CSS implementation has grown with the movement of JavaScript and the web as a platform.

Since React’s release in 2013, component-built web applications have become the norm. The implementation of CSS has, in turn, been questioned. The main argument against using CSS in-line with React has been the separation of concerns (SoC). SoC is a design principle that describes the division of a program into sections, each of which addresses a different concern. This principle was used mainly when developers kept the three main web technologies in separate files: styles (CSS), markup (HTML) and logic (JavaScript).

This changed with the introduction of JSX in React. The development team argued that what we had been doing was, in fact, the separation of technologies, not concerns. One could ask, since JSX moved the markup into the JavaScript code, why should the styles be separate?

As opposed to divorcing styles and logic, different approaches can be used to merge them in-line. An example of this can be seen below:

<button style="background: red; border-radius: 8px; color: white;">Click Me</button>

In-line styles move the CSS definitions from the CSS file. This thereby removes the need to import the file and saves on bandwidth, but sacrifices readability, maintainability, and style inheritance.

CSS Modules

button.css

.button {
  background: red;
  border-radius: 8px;
  color: white;
}

button.js

import styles from './button.css';
document.body.innerHTML = `<button class="${styles.button}">test</button>`;

As we can see from the code example above, the CSS still lives in its own file. However, when CSS Modules is bundled with Webpack or another modern bundler, the CSS is added as a script tag to the HTML file. The class names are also hashed to provide a more granular style, resolving the problems that come with cascading style sheets.

The process of hashing involves generating a unique string instead of a class name. Having a class name of btn will result in a hash of DhtEg which prevents styles cascading and applying styles to unwanted elements.

index.html

<style>
.DhtEg {
  background: red;
  border-radius: 8px;
  color: white;
}
</style><button class="DhtEg">test</button>

From the example above we can see the style tag element added by CSS Modules, with the hashed class name and the DOM element we have that uses the hash.

Glamor

Glamor is a CSS-in-JS library that allows us to declare our CSS in the same file as our JavaScript. Glamor, again, hashes the class names but provides a clean syntax to build CSS style sheets via JavaScript.

The style definition is built via a JavaScript variable that describes each of the attributes using camel case syntax. The use of camel case is important as CSS defines all attributes in train case. The main difference is the change of the attribute name. This can be an issue when copying and pasting CSS from other parts of our application or CSS examples. For example overflow-y would be changed to overFlowY. However, with this syntax change, Glamor supports media queries and shadow elements, giving more power to our styles:

button.js

import { css } from 'glamor';

const rules = css({
  background: red;
  borderRadius: 8px;
  color: 'white';
});

const button = () => {
  return <button {...rules}>Click Me</button>;
};

styled-components

styled-components is a new library that focuses on keeping React components and styles together. Providing a clean and easy-to-use interface for styling both React and React Native, styled-components is changing not only the implementation but the thought process of building styled React components.

styled-components can be installed from npm via:

npm install styled-components

Imported as any standard npm package:

import styled from 'styled-components';

Once installed, it’s time to start making styled React components easy and enjoyable.

Building Generic Styled React Components

Styled React components can be built in many ways. The styled-components library provides patterns that enable us to build well-structured UI applications. Building from small UI components — such as buttons, inputs, typography and tabs — creates a more unified and coherent application.

Using our button example from before, we can build a generic button using styled-components:

const Button = styled.button`
  background: red;
  border-radius: 8px;
  color: white;
`;

class Application extends React.Component {
  render() {
    return (
      <Button>Click Me</Button>
    )
  }
}

Codepen

As we can see, we’re able to create our generic button while keeping the CSS in-line with the JavaScript. styled-components provides a wide range of elements that we can style. We can do this by using direct element references or by passing strings to the default function.

const Button = styled.button`
  background: red;
  border-radius: 8px;
  color: white;
`;

const Paragraph = styled.p`
  background: green;
`;

const inputBg = 'yellow';
const Input = styled.input`
  background: ${inputBg};
  color: black;
`;

const Header = styled('h1')`
  background: #65a9d7;
  font-size: 26px;
`

class Application extends React.Component {
  render() {
    return (
      <div>
        <Button>Click Me</Button>
        <Paragraph>Read ME</Paragraph>
        <Input
          placeholder="Type in me"
        />
        <Header>I'm a H1</Header>
      </div>
    )
  }
}

Codepen

The main advantage to this styling method is being able to write pure CSS. As seen in the Glamor example, the CSS attribute names have had to be changed to camel case, as they were attributes of a JavaScript object.

styled-components also produces React friendly primitives that act like the existing elements. Harnessing JavaScript template literals allows us to use the full power of CSS to style components. As seen in the input element example, we can define external JavaScript variables and apply these to our styles.

With these simple components, we can easily build a style guide for our application. But in many cases, we’ll also need more complicated components that can change based on external factors.

Customizable Styled React Components

The customizable nature of styled-components is the real strength. This could commonly be applied to a button that needs to change styles based on context. In this case, we have two button sizes — small and large. Below is the pure CSS method:

CSS

button {
  background: red;
  border-radius: 8px;
  color: white;
}

.small {
  height: 40px;
  width: 80px;
}

.medium {
  height: 50px;
  width: 100px;
}

.large {
  height: 60px;
  width: 120px;
}

JavaScript

class Application extends React.Component {
  render() {
    return (
      <div>
        <button className="small">Click Me</button>
        <button className="large">Click Me</button>
      </div>
    )
  }
}

Codepen

When we reproduce this using styled-components, we create a Button component that has the basic default style for the background. As the component acts like a React component, we can make use of props and change the style outcome accordingly:

const Button = styled.button`
  background: red;
  border-radius: 8px;
  color: white;
  height: ${props => props.small ? 40 : 60}px;
  width: ${props => props.small ? 60 : 120}px;
`;

class Application extends React.Component {
  render() {
    return (
      <div>
        <Button small>Click Me</Button>
        <Button large>Click Me</Button>
      </div>
    )
  }
}

Codepen

Advanced Usage

styled-components provides the ability to create complex advanced components, and we can use existing JavaScript patterns to compose components. The example below demonstrates how components are composed — in the use case of notification messages that all follow a basic style, but each type having a different background color. We can build a basic, styled component and compose on top to create advanced components:

const BasicNotification = styled.p`
  background: lightblue;
  padding: 5px;
  margin: 5px;
  color: black;
`;

const SuccessNotification = styled(BasicNotification)`
  background: lightgreen;
`;

const ErrorNotification = styled(BasicNotification)`
  background: lightcoral;
  font-weight: bold;
`;

class Application extends React.Component {
  render() {
    return (
      <div>
        <BasicNotification>Basic Message</BasicNotification>
        <SuccessNotification>Success Message</SuccessNotification>
        <ErrorNotification>Error Message</ErrorNotification>
      </div>
    )
  }
}

Codepen

As styled-components allows us to pass standard DOM elements and other components, we can compose advanced features from basic components.

Component Structure

From our advanced and basic example, we can then build a component structure. Most standard React applications have a components directory: we place our styled components in a styledComponents directory. Our styledComponents directory holds all the basic and composed components. These are then imported into the display components used by our application. The directory layout can be seen below:

src/
  components/
    addUser.js
    styledComponents/
      basicNotification.js
      successNotification.js
      errorNotification.js

Conclusion

As we’ve seen in this post, the ways in which we can style our components varies greatly — none of which is a clear winning method. This article has shown that styled-components has pushed the implementation of styling elements forward, and has caused us to question our thought processes with regards to our approach.

Every developer, including myself, has their favorite way of doing things, and it’s good to know the range of different methods out there to use depending on the application we’re working on. Styling systems and languages have advanced greatly throughout the years, and there’s no question that they are sure to develop further and change more in the future. It’s a very exciting and interesting time in front-end development.

What’s your preferred way to style React components, and why?


This article was peer reviewed by Vildan Softic. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

Frequently Asked Questions on Styling React Components with Styled Components

What are the benefits of using styled-components in React?

Styled-components is a library for React and React Native that allows you to use component-level styles in your application that are written with a mixture of JavaScript and CSS. This approach has several benefits. Firstly, it enables you to write actual CSS code in your JavaScript, allowing for easier component-specific styling. Secondly, it eliminates the mapping between components and styles. This means that when you’re defining your styles, you’re actually creating a normal React component, which has your styles attached to it. Lastly, styled-components also automatically handle vendor prefixing, making your styles compatible with older browser versions.

How do I install styled-components in my React project?

To install styled-components in your React project, you need to run the following command in your terminal: npm install --save styled-components. This command installs the styled-components library and saves it in your project dependencies. After installation, you can import it into your React component file using the import statement: import styled from 'styled-components';.

How can I use props in styled-components?

Props in styled-components can be used to alter the styles based on the component’s props. This is done by passing a function to your styled component that takes props as an argument and returns the CSS you want to apply. For example, you can change the background color of a button based on a prop like this:
const Button = styled.button`
background: ${props => props.primary ? 'palevioletred' : 'white'};
color: ${props => props.primary ? 'white' : 'palevioletred'};
`;
In this example, if the primary prop is passed to the Button component, it will have a palevioletred background and white text.

Can I use styled-components with React Native?

Yes, styled-components is compatible with both React and React Native. The syntax and usage are almost identical, with the main difference being the set of components that you style. In React Native, instead of styling divs, sections, and articles, you’re styling Views, Texts, and Image components.

How can I handle global styles with styled-components?

Styled-components provides a helper function called createGlobalStyle for defining global styles. This function generates a React component that, when used, applies the styles to the entire document. Here’s an example of how to use it:
import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
`;

// In your component render method
<React.Fragment>
<GlobalStyle />
<YourComponent />
</React.Fragment>
In this example, the GlobalStyle component applies a set of styles to the body element of the document.

How can I use media queries with styled-components?

Media queries in styled-components can be used just like you would in regular CSS. You can include your media query right inside your styled component. Here’s an example:
const Box = styled.div`
width: 100%;
padding: 16px;
@media (min-width: 768px) {
width: 50%;
}
`;
In this example, the Box component will have a width of 100% on screens smaller than 768px, and a width of 50% on screens larger than 768px.

Can I use CSS animations with styled-components?

Yes, you can use CSS animations with styled-components. The library provides a helper function called keyframes that you can use to define your animations. Here’s an example:
import styled, { keyframes } from 'styled-components';

const fadeIn = keyframes`
from {
opacity: 0;
}
to {
opacity: 1;
}
`;

const FadeInDiv = styled.div`
animation: 1s ${fadeIn} ease-out;
`;
In this example, the FadeInDiv component will fade in over a duration of 1 second.

How can I extend styles with styled-components?

Styled-components provides a method called extend that allows you to extend the styles of a component. This is useful when you want to create a new component that shares some styles with an existing one, but also has some of its own unique styles. Here’s an example:
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;

const TomatoButton = Button.extend`
color: tomato;
border-color: tomato;
`;
In this example, the TomatoButton component shares all the styles of the Button component, but overrides the color and border-color properties.

Can I use styled-components with existing CSS files?

Yes, you can use styled-components alongside existing CSS files. However, one of the main benefits of styled-components is the ability to colocate your styles with your components, eliminating the need for separate CSS files. If you have existing CSS that you want to use, you can gradually migrate it to styled-components.

How can I debug styled-components?

Styled-components provides a helpful debugging feature through the use of source maps. When you inspect a styled component in the browser’s developer tools, you’ll see the generated class name and the file and line number where the component is defined. This makes it easy to locate and debug your styled components.