Laptop and mobile phone on a desk

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:

  1. 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.
  2. CSS Modules: Use CSS stylesheets, but scope them to the components using tooling.
  3. 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:

  1. Globally style 'a' tags in the above GlobalStyle
  2. Add an AppLink styled-component to App.js
  3. 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.

New Call-to-action
blog comments powered by Disqus
Times
Check

Success!

Times

You're already subscribed

Times