Styling in React: From External CSS to Styled Components
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.
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>
)
}
}
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>
)
}
}
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>
)
}
}
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>
)
}
}
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>
)
}
}
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!