Build React Apps with Styled Components: The Why and How of the Styled-Components Library

React is a library that helps developers approach user interface (UI) development with the single-responsibility principle in mind, while building reusable, encapsulated components. But when it comes to styling, there is a heated debate in the React community. There are a few schools of thought:
- Plain Old Stylesheets: The thinking here is that there are already too many CSS architecture solutions out there — so there’s no need to do styling in React any differently.
- CSS Modules: Use CSS stylesheets, but scope them to the components using tooling.
- CSS in JS: In this case, forget stylesheets — building apps in React is enough of a paradigm shift to justify a similar shift in the way we approach styling. Instead, fully couple the components with the styling by actually writing the styles in Javascript.
To build an app the “React Way,” we are asked to break a design up into components — pieces of UI that are responsible for just one thing. Aside from being a scalable and maintainable way to build complex applications, it also encourages reuse of components between projects. I believe that CSS in JS is the best approach to styling React components. In React, there is no meaningful separation of concerns between the appearance of a component and the logic that dictates its behavior.
Enter styled-components. I’ve found the best way to explain
styled-components is to start using it, so let’s dive right in. We’re
going to convert the classic create-react-app
(CRA) generated start
page to styled-components, so the first thing to do is generate our app:
Set Up
$ npx create-react-app my-first-styled-components
$ cd my-first-styled-components
Then install styled-components, open up the project in your favorite text-editor (I use VSCode), and get the app running locally:
$ npm i styled-components
$ npm run start
Observe the classic CRA generated starting page with its spinner React
logo, HTML5 'code' tag, and just about nothing else. Step one is to
remove all the classNames
in App.js
, as well as the ./App.css
import. Remove the index.css
import from index.js
as well. Let’s
keep the actual files index.css
and App.css
for reference. If you
look at the page now, you’ll have a giant inanimate logo and some
(nearly) un-styled text below.
Start Styling
Styled-components makes use of an ES6 feature, tagged template
literals, to create a
React component from a CSS-like string. Let’s convert our encapsulating
div
into a styled component.
// ...other imports
import styled from "styled-components";
// ...App functional component
const AppStyled = styled.div``;
export default App;
We’ve created our styled component and named it AppStyled. It’s a good
convention to name our outermost styled component the name of the
component, App
in our case, plus “Styled”. This avoids collision with
the App
function, but leaves little room for confusion. Next, let’s
use the styled component in our jsx.
// ...imports
function App() {
return (
<AppStyled>
<header>{/* ...etc */}</header>
</AppStyled>
);
}
Next step is to add our styles. Before we do that, I encourage you to
find a styled-components syntax highlighter for your text-editor. For
VSCode I use jpoissonnier.vscode-styled-components
. We’ll also add
another styled component to App.js
. Open up App.css
and keep that
tab handy.
// ...imports
function App() {
return (
<AppStyled>
<AppHeader>{/* ...etc */}</AppHeader>
</AppStyled>
);
}
const AppStyled = styled.div`
/* Pasted from .App in App.css */
text-align: center;
`;
const AppHeader = styled.header`
/* Pasted from .App-header in App.css */
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
`;
export default App;
Getting Modular
One concern people have when starting out with styled-components is that
their jsx files can get pretty lengthy. I think that's a great concern
to have, and the solution helps us keep things "React-y". Rather than
adding any more styled-components to App.js
, let's break our
remaining styles out into separate components. You can do this any way
you want, but here we're going to create two new files:
| src/components/Logo.js
| src/elements/keyframes.js
Add Animation
Let's use styled-component's keyframes
function to capture the logo
animation in its own export. Inside src/elements/keyframes.js
:
import { keyframes } from "styled-components";
export const spin = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
Now we have a spin animation we can import anywhere we want! Next,
remove the 'img' tag from App.js
, and update Logo.js
as follows:
import React from "react";
import styled from "styled-components";
import logo from "../logo.svg";
import { spin } from "../elements/keyframes";
const Logo = () => <LogoStyled src={logo} alt="logo" />;
const LogoStyled = styled.img`
height: 40vmin;
pointer-events: none;
animation: ${spin} infinite 20s linear;
`;
export default Logo;
The thing to remember about styled-components tagged template literal syntax is that any valid Javascript can be inserted into it as we did here with our “spin” animation. As long as the expression resolves to something that can be coerced to a string, you’re free to be as verbose as you need.
Let’s bring it in to App.js
:
// other imports
import Logo from "./components/Logo";
function App() {
return (
<AppStyled>
<AppHeader>
<Logo />
{/* ...etc */}
</AppHeader>
</AppStyled>
);
}
Here we’ve imported the Logo component and replaced the 'img' tag in
App.js
with our <Logo />
component. Now save and check it out in the
browser.
Global Styles
But do we have to create styled-components for every single element in
our app? Not necessarily. Styled-components has a function,
createGlobalStyle
, that is the perfect place to define your typography
hierarchies and include any other global styles you want.
Create src/elements/GlobalStyle.js
, and add the following, including
css from index.css
:
import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}
`;
export default GlobalStyle;
Then in App.js
:
// other imports
import GlobalStyle from "./elements/GlobalStyle";
function App() {
return (
<React.Fragment>
<GlobalStyle />
<AppStyled>{/* ...etc */}</AppStyled>
</React.Fragment>
);
}
Wrapping it Up
React.Fragment can also be shortened to <>
and </>
and are necessary
for reasons described here.
With that we're nearly done! All that's left is to style the anchor
tag. We have several options:
- Globally style 'a' tags in the above
GlobalStyle
- Add an
AppLink
styled-component toApp.js
- Create a
Link
component for export
For the sake of showing a variety of ways to use styled-components,
we're going to use option three. Create src/elements/elements.js
, and
inside add the following:
import styled from "styled-components";
const colorLink = "#61dafb";
export const Link = styled.a`
color: ${colorPrimary};
text-decoration: none;
&:hover {
text-decoration: underline;
}
`;
// export const SomeOtherElement = styled.p``;
We’re styling our Link element with a color defined as a constant — this would generally live elsewhere with other style constants. We've also spiced up the anchor tag ever so slightly to demonstrate pseudo classes using a SASS-like syntax.
Then we can import our Link in App.js
as follows:
// other imports
import { Link } from "./elements/elements";
function App() {
return (
<React.Fragment>
<GlobalStyle />
<AppStyled>
<AppHeader>
<Logo />
<p> … </p>
<Link href="https://reactjs.org" etc />
{/* ...etc */}
</AppHeader>
</AppStyled>
</React.Fragment>
);
}
Here we’ve replaced the 'a' tag in App.js
with our <Link />
element. Be sure to keep all the attributes, href, etc.
And that does it!
Styled-components is a perfect fit for React. In my experience, the best React tools guide the developer toward the "React Way" of doing things. Styled-components does this better than any other style system available.