We're live-coding on Twitch! Join us!

Converting CSS In React to Styled Components

Code

Styled Components is one of those interesting topics that developers love to argue about. It's a brand new way of doing things that many people just aren't ready to acknowledge. Regardless, just because something is different doesn't mean it isn't worth a try.

In this article, we are going to convert a React site using regular CSS to one that uses Styled Components. Don't worry... if you don't like it, you never have to use it again! 😜

What and Why - Styled Components

Again, Styled Components are debated by many, but there are some pretty cool benefits.

  • automatic vendor prefixing
  • scoping for CSS
  • leverage JavaScript syntax for dynamic CSS
  • benefits of a pre-compiler like variables and nesting

You can find more on the Styled Components Motivation Page.

Lots of people debate about whether or not JS in CSS is a good idea. Just do whatever works best for you and your team!

Personally, I like the idea of Styled Components for two main reasons. First, I get to treat my styles like everything else in my React projects, a component. I can pass properties, reuse them, etc. It just feels nice!

Additionally, I get to leverage JavaScript syntax that I am already familiar with to add dynamic updates to my CSS. One of the things that bothered me about pre-compilers (LESS, SASS, etc.) is that they each have their own syntax for certain things on top of CSS. With Styled Components, I just write JavaScript for those additional features.

The Starter Project

There are two projects in Code Sandbox that you can use, a starter project and a finished project. The starter project is setup with React Router and has a navbar that links to two different pages, the home page and the pricing page. Although there are other links in the navbar, they don't work. They are just there for demo purposes 😀.

Install The Styled Components Library

Before we create a Styled Component we need to install the library. You can do this by running the following command in the terminal or using the GUI in Code Sandbox.

npm install styled-components

With the package installed, our goal is to clear our the index.css file in the starter project by converting fully to Styled Components. In the end, both projects have the same functionality.

Let's get moving!

Create Styled Component for Container

Let's start with something simple, a container of wrapper for our app. First, create a folder called Styled in the src directory. We will store all of our Styled Components inside of this directory.

Then, create a file called Container.js inside of that directory. Now, import the styled object from the library.

import  styled  from  'styled-components';

To create a Styled Component we use styled follow by a period and then the type of DOM element we are trying to style. In this case a div. Then we use tagged template literals (open and close backticks) to put our styles inside of. You can copy all the styles that we reference from here or from the starter files.

Make sure your styled components are capitalized so that React can recognize them as custom components.

export  const  Container  =  styled.div`max-width: 90vw;
    margin: 0  auto;`

Congrats, you created your first component. Now, we need to use it. Inside of the App.js page, we need to import the Container Styled Component.

import  {  Container  }  from  "./Styled/Container";

Then replace the existing div with a className of container with your actual Container component like so.

<div  className="container">
    <Route  path="/"  exact  component={Home} />
    <Route  path="/pricing"  component={Pricing} />
</div>

becomes

<Container>
    <Route  path="/"  exact  component={Home} />
    <Route  path="/pricing"  component={Pricing} />
</Container>

Not so bad huh? We are basically going to do this through the rest of our app, ripping out all of the CSS from the index.css file into their own Styled Components. We will also learn a few cool tricks along the way!

Create Styled Components for Navbar

Now, let's look at the Navbar. Here's the starter CSS.

.nav {
    display: flex;
    padding: 20px;
    align-items: center;
    margin-bottom: 40px;
}

@media  (max-width: 786px) {
    .nav {
        flex-direction: column;
    }
}

.nav-brand {
    flex-grow: 1;
    font-size: 24px;
    font-weight: 700;
    color: #333;
    text-decoration: none;
}

.nav-items {
    list-style: none;
    padding-inline-start: 0;
    display: flex;
    align-items: center;
}

.nav-item {
    margin-right: 20px;
    cursor: pointer;
    transition: 250ms;
    cursor: pointer;
    color: #333;
    text-decoration: none;
}

.nav-item:hover {
    transform: scale(1.05);
}

.link-button {
    padding: 10px  10px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    border: 1px  solid  #663399;
}

.link-button-primary {
    background: #663399;
    color: white;
}

Based on that, we need to create several components.

  • Nav
  • NavBrand
  • NavItems
  • NavItem (with hover)
  • NavItemButton (with additional properties for primary)

So, let's start from the top. Create a new file in the Styled directory called Navbar.js and import styled at the top. Now, let's create the Nav component. It's a nav DOM element, so it will look like this.

export const Nav = styled.nav`
`

Now, we can basically copy over our nav styles.

export const Nav = styled.nav`display: flex;
    padding: 20px;
    align-items: center;
    margin-bottom: 40px;

    @media (max-width: 786px) {
        flex-direction: column;
    }`;

As you can see, we even nested in our media query just like you would with a pre-compiler. Pretty cool huh? Nesting is one of my favorite features!

Nesting your styles is one of my favorite features and helps with readability and organization

Next up is the NavBrand, but the problem is that this is actually a Link component from React Router instead of a basic DOM element. No worries, we can tweak our syntax to style the Link component directly like so. Make sure, you import Link at the top of your file.

export const NavBrand = styled(Link)`
`;

From there, now we just copy over our styles again.

export const NavBrand = styled(Link)`flex-grow: 1;
    font-size: 24px;
    font-weight: 700;
    color: #333;
    text-decoration: none;`;

Now for the NavItems component which will style a ul element.

export const NavItems = styled.ul`list-style: none;
    padding-inline-start: 0;
    display: flex;
    align-items: center;`;

Now NavItem which is also styling the Link component as the NavBrand did above. Notice, also that we are able to nest the hover state right inside of the component itself.

export const NavItem = styled(Link)`margin-right: 20px;
    cursor: pointer;
    transition: 250ms;
    cursor: pointer;
    color: #333;
    text-decoration: none;

    &:hover {
        transform: scale(1.05);
    }`;

And lastly, the NavItemButton. We want this element to include all of the styles we just defined for NavItem in addition to a few more. In this case, we can extend the NavItem component like so.

export const NavItemButton = styled(NavItem)`padding: 10px 10px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    border: 1px solid #663399;
    /* add additional styles for primary*/`;

This works well for a regular NavItemButton, but what if we wanted one that adds even a few more special properties? Since these are basically React components, they can accept props just like any other component. Because of that, we can add some JavaScript to check whether or not the button has a property of primary , and if so, add a few more properties. Here's how we do it.

export const NavItemButton = styled(NavItem)`padding: 10px 10px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    border: 1px solid #663399;
    /* add additional styled if primary*/
    ${(props) =>
        props.primary &&
        css
            background-color: #663399;
            color: white;
        };`

The syntax might look a bit funky, so let's explain. We define a function that takes in props and then returns a bit of css if that primary property is present. To define the additional CSS, we use the css object from the styled-components library. Make sure to import it at the top.

import styled, { css } from 'styled-components';

This is part of the beauty of Styled Components. Use the JavaScript skills that you already know to customize your CSS dynamically. You can check the final version of these componts in the finished source code.

You will also need to update your Navbar component to use these new Styled Components.

import React from 'react';
import {
    Nav,
    NavBrand,
    NavItems,
    NavItem,
    NavItemButton
} from '../Styled/Navbar';

export default function NavbarStyledComponents() {
    return (
        <Nav>
            <NavBrand to="/">Cool Product</NavBrand>
            <NavItems>
                <NavItem to="/pricing">Pricing</NavItem>
                <NavItem>Docs</NavItem>
                <NavItemButton>Log in</NavItemButton>
                <NavItemButton primary>Get Started For Free</NavItemButton>
            </NavItems>
        </Nav>
    );
}

Create Styled Components for Split Component

Now on to the Split Component which is used on the home page to create a 50/50 split between two divs of content. Start by creating the Split.js file inside of the Styled directory.

We will need to create three different components here.

  • Split
  • SplitTitle
  • SplitImg

Import styled at the top of the page and stub our the Split component.

export const Split = styled.div`
`

Then, copy over the CSS styles. Remeber that we can still embed the media query right inside of the component.

By nesting media queries, we can explicitly see what changes occurr for that specific compenent

export const Split = styled.div`display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: center;
    grid-gap: 20px;

    @media (max-width: 786px) {
        grid-template-columns: 1fr;
    }`

The last two of these are pretty simple. Hopefully, you're getting the hang of this by now.

export const SplitTitle = styled.h1`font-size: 42px;`

and


export const SplitImg = styled.img`height: 100%;
    width: 100%;`

Now, delete that CSS from the CSS file and update the Home component to leverage these new Styled Components.

import React from 'react';
import computer from '../computer.png';
import { SplitImg, Split, SplitTitle } from '../Styled/Split';

export default function Home() {
    return (
        <Split>
            <div>
                <SplitTitle>Cool, Catchy Slogan</SplitTitle>
                <p>
                    Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                    Error, fugit provident. Fugit, distinctio dolor nesciunt
                    natus quidem laborum beatae ratione accusantium hic illo
                    quas id numquam possimus, similique odit alias.
                </p>
                <p>
                    Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                    Error, fugit provident. Fugit, distinctio dolor nesciunt
                    natus quidem laborum beatae ratione accusantium hic illo
                    quas id numquam possimus, similique odit alias.
                </p>
            </div>
            <div>
                <SplitImg src={computer} alt="" />
            </div>
        </Split>
    );
}

Create Styled Components for Pricing Page

The next major section is for the Pricing Page. Here's the components that we need to create.

  • Pricing (containers for PricingCards)
  • PricingCard
  • PricingTitle
  • PricingFeatures
  • Price

Create the Pricing.js file inside of the Styled directory, then start with the Pricing component. This use CSS Grid to show three different PricingCards.

export const Pricing = styled.div`display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-gap: 10px;`

Now for the PricingCard itself. This component also has a hover effect, so we can throw it right inside of the component itself.

export const PricingCard = styled.div`background: white;
    padding: 20px;
    border-radius: 5px;
    box-shadow: 1px 1px 5px #ccc;
    text-align: center;

    &:hover {
        box-shadow: 3px 3px 5px #ccc;
        transform: translateY(-2px);
    };`

I'm sure this process is getting a bit old, so here are the last three components. There's nothing special about these, although you should be aware of the DOM elements they are targeting.

export const PricingTitle = styled.h2`text-align: center;
    font-weight: 300;`

export const PricingFeatures = styled.ul`text-align: left;
    margin-bottom: 60px;`

export const Price = styled.span`text-align: center;
    font-size: 54px;
    margin-top: 30px;
    margin-bottom: 60px;`

Now that these components are defined, update the JSX in the Pricing page to use these instead of the CSS classes we used previously.

<Pricing>
    <PricingCard>
        <PricingTitle>Free</PricingTitle>
        <p>
            <Price>$0</Price>/month
        </p>{' '}
        <PricingFeatures>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
        </PricingFeatures>
        <Button>Get Started</Button>
    </PricingCard>
    <PricingCard>
        <PricingTitle>Standard</PricingTitle>
        <p>
            <Price>$10</Price>/month
        </p>{' '}
        <PricingFeatures>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
        </PricingFeatures>
        <Button primary>Get Started</Button>
    </PricingCard>
    <PricingCard>
        <PricingTitle>Enterprise</PricingTitle>
        <p>
            <Price>$100</Price>/month
        </p>
        <PricingFeatures>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
        </PricingFeatures>
        <Button>Get Started</Button>
    </PricingCard>
</Pricing>

Create Global CSS

With all of those components created, we've still got a little bit of CSS to clean up.

body {
  margin: 0;
  font-family: sans-serif;
  color: #333;
  background: #eee;
}

In React using Styled Components, we can't directly target the body element, so this is where the concept of Global Styles comes into play. We can use the createGlobalStyle object from the styled-components library to generate the typical "non-scoped" CSS that we started with originally. This is exactly what we need to finish out this migration.

Create a new file called Global.js insdie of the Styled directory and import createGlobalStyle . We can then use it to define the global CSS for our project.

export const GlobalStyle = createGlobalStyle`body {
        margin: 0;
        font-family: sans-serif;
        color: #333;
        background: #eee;
    };`

Now, we need to import these styles somewhere in our app. We can do this in our App.js file by just including the component just inside the Router tag.

import React from "react";
import Navbar from "./components/Navbar";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Home from "./pages/Home";
import Pricing from "./pages/Pricing";
import { Container } from "./Styled/Container";
import { GlobalStyle } from "./Styled/Global";
function App() {
  return (
    <Router>
      <GlobalStyle />
      <Navbar />
      <Container>
        <Route path="/" exact component={Home} />
        <Route path="/pricing" component={Pricing} />
      </Container>
    </Router>
  );
}

export default App;

Wrap Up

I'm sure that seemed a bit repetitive, but I think that's the point. Once you do it a few times, you get used to it pretty quickly and breeze through it. Styled Components are just a different way to write CSS in React that come with a few extra benefits.

So, are you using Styled Components? Are you an advocate for CSS in JS? Or are you on the opposite side of the fence? Let us know in the comments!

Like this article? Follow @jamesqquick on Twitter