Yusuff Faruq Frontend web developer and anime lover from Nigeria.

Using styled-components in React

6 min read 1680

Styled-components in React Logo with an Emoji

With the advent of modern component-based frontend frameworks, the need to write CSS that is scoped to a particular component has increased. And as with most problems in the frontend sphere, a variety of solutions have sprung up to address it.

The most common solution is CSS-in-JS, a pattern that involves writing component and app styles (CSS) in JavaScript. There are a number of CSS-in-JS libraries out there today, but perhaps the most popular and enduring one — and the focus of this post — is styled-components.

Styled-components is a CSS-in-JS library that drastically improves developer experience for the modern frontend developer while providing a near-perfect user experience. Aside from giving developers the ability to write component-scoped CSS, styled-components comes with a number of other benefits, including:

  • Automatic vendor prefixing
  • Unique class names for each styled component
  • Easier maintenance of styles — developers can delete or modify styles without affecting other components

Now that we’re properly introduced to the styled-components library, let’s take a look at a basic use case, which involves using the styled API.

The styled API: Where it all begins

The styled API allows us to create a StyledComponent — a component with styles, as the name implies — by using either a regular HTML element or another StyledComponent as a base.

Let’s take a look at the first method. If we want to make a styled div element and a styled <h1> element into React components, we can simply write:

import React from "react";
import styled from "styled-components";
const StyledDiv = styled.div`
  padding: 1.5rem;
  background-color: skyblue;
`
const StyledH1 = styled.h1`
  color: blue;
`

And with that, we have two React components that we can now use as regular components:

function App() {
  return (
      <StyledDiv>
        <StyledH1>A styled H1 element</StyledH1>
      </StyledDiv>
  );

The result in the browser should look like this:

Styled H1 Element Example

That’s how easy it is to style our components!

Note: You may have noticed that the styled API makes use of backticks (“). This a feature in ES6 called tagged template literals; it’s the same as calling a function.

As I mentioned earlier, we can also create a new StyledComponent by using other StyledComponents as the base. So if we wanted to create another StyledComponent that has all the styling properties of <StyledDiv> but with more rounded borders, we could do this:

const StyledRoundedDiv = styled(StyledDiv)`
  border-radius: 30px;
`

So now <StyledRoundedDiv> inherits all the styles of <StyledDiv>, but with its own style for adjusting the border-radius property. With this feature, we can create different components that all inherit styles from a single base component.

The styled API also allows us to create StyledComponents by using JavaScript to style objects (commonly used in JSX). As stated in the documentation, “This is particularly useful when you have existing style objects and want to gradually move to styled-components.”

If we were to recreate our StyledDiv component using style objects, our code would look like this:

const StyledDiv = styled.div({
  padding: "1.5rem",
  backgroundColor: "skyblue"
})

Dynamic styling using React props

Another powerful feature of styled-components is its ability to alter a component’s styles using props. To better illustrate this concept, let’s take a look at the Material-UI React library. We have a <Button> component that takes in different props, but we will only focus on the variant and color props.

With the variant prop, we can determine whether we want our button to be outlined or just filled with the color specified in the color prop. With the color prop, we can specify the color of the outline or the color of the button, depending on which value we pass to the variant prop.

If we were to implement such a component using styled-components, our code would probably look a lot like this:

const Button = styled.button`
  border-radius: 4px;
  padding: 6px 16px;
  min-width: 64px;
  text-transform: uppercase;
  font-weight: 500;
  font-size: 0.875rem;
  letter-spacing: 0.02857em;
  line-height: 1.75;
  font-family: "Roboto", "Helvetica", "Arial", sans-serif;
  background-color: ${(props) => backgroundColor(props)};
  ${(props) => colorAndBorder(props)}
`;
const colorToValue = {
  primary: "#1976d2",
  secondary: "rgb(220, 0, 78)",
};
const colorAndBorder = (props) => {
  var finalColor = "white";
  if (props.variant == "outlined") {
    if (colorToValue[props.color]) {
      finalColor = colorToValue[props.color];
    } else {
      finalColor = "black";
    }
  } else if (props.variant == "contained") {
    if (props.color) {
      finalColor = "white";
    } else {
      finalColor = "black";
    }
  }
  return css`
    color: ${finalColor};
    border: ${(prop) =>
      props.variant == "outlined" ? "1px solid " + finalColor : "none"};
  `;
};
const backgroundColor = (props) => {
  if (props.variant == "outlined") {
    return "white";
  } else if (props.variant == "contained") {
    if (props.color) {
      let color = colorToValue[props.color];
      if (color) {
        return color;
      } else {
        return "#e0e0e0";
      }
    } else {
      return "#e0e0e0";
    }
  }
};

From the above code block, you can see that I had base styles for the Button component and dynamic styles for the background-color, color, and border properties, depending on the props.

Since the styled and css APIs make use of tagged template literals, we can “inject” JavaScript (using string interpolation) into our CSS to dynamically style components, which we did using two custom functions: backgroundColor and colorAndBorder.

backgroundColor takes in the StyledComponent‘s props and returns the suitable background color of the button as a string. The outer function in the Button component styles can now use that string to style the button.

For the color and border properties, however, we used a function that returns a value we can use directly in our Button‘s styles string instead of just a string to be used by an outer function. This is where the css helper function comes in.



The css helper function generates a value that can be used directly in template literal styles, especially when the template literal passed to the css function contains string interpolation.

We can now use our Button component like so:

function App() {
  return (
    <>
      <Button variant="contained" color="primary">
        Primary Contained
      </Button>

      <Button variant="outlined" color="secondary">
       Secondary Outlined
      </Button>

      <Button variant="contained">
       Default contained
      </Button>
    </>
  );
}

Additional styling methods with the attrs constructor

styled-components also allows additional props or HTML attributes to be added to a component using the attrs method. Using attrs, we can define static props or attributes like the type of an <input> element as well as dynamic props. Here’s an example of the attrs method in use:

const Link = styled.a.attrs((props) => ({
  href: props.$to,
}))`
  text-decoration: none;
  color: #000;
  border: 1px solid #000;
  padding: 1rem 1.5rem;
  border-radius: 5px;
`;

function App() {
  return (
    <>
      <Link $to="https://blog.logrocket.com">Test link</Link>
    </>
  );
}

In the above code, we attached an href attribute to our <Link> component, whose underlying HTML element was an anchor element. The value passed to the href element was received from the transient prop, $to, of our <Link> component.

Transient props are a feature in styled-components that allow us to use props that will not show up on the DOM. So if you inspect our Link component, you will notice that it was rendered as simply:

<a href="https://blog.logrocket.com" class="sc-bdfBwQ geNjDe">Test link</a>

Theming React apps with styled-components

styled-components also supports theming through the use of a ThemeProvider component. We can pass a theme prop to the <ThemeProvider>, and this theme will be passed to all the children of the <ThemeProvider> as a prop.

We can now choose to dynamically style those child components based on the value of the theme prop. Here’s a simple example to show how this works:

const lightTheme = {
  main: "#fff",
};
const darkTheme = {
  main: "#000",
};
const invertColor = (props) => {
  if (props.theme.main == "#000"){
    return "#fff"
  } else {
    return "#000"
  }
}
const Div = styled.div`
  padding: 4rem;
  background-color: ${props => props.theme.main}
`
const Button = styled.button`
  padding: 1.5rem 2rem;
  color: ${props => invertColor(props)};
  border: 1px solid ${props => invertColor(props)};
  background-color: ${props => props.theme.main};
  transition: all 0.5s;
  &:hover{
    color: ${props => props.theme.main};
    background-color: ${props => invertColor(props)};
    border: 1px solid ${props => props.theme.main};
  }
`
/*Default props for the Button component in case it has no theme attached to its props
*/
Button.defaultProps = {
  theme: darkTheme
}
function App() {
  return (
    <>
    <ThemeProvider theme = {lightTheme}>
      <Div>
        <Button>Light Theme button</Button>
      </Div>
    </ThemeProvider>
    <ThemeProvider theme = {darkTheme}>
      <Div>
        <Button>Dark Theme button</Button>
      </Div>
      <Button>Dark Theme button</Button>
    </ThemeProvider>

    </>
  );
}

The result of the code block above in the browser should be:

A Code Block With Light and Dark Themes

As you can see from the code, there are two objects, lightTheme and darkTheme, to represent the primary colors for the light and dark themes, respectively.

There’s also a function called invertColor, which simply takes in props and returns the opposite primary color of the theme attached to the props. Using the theme objects and the invertColor function, we were able to create a light and dark theme and style our components accordingly.

Also notice that we used the ampersand (&) symbol in the template literal of the <Button> component; its usage is similar to what you would find in a CSS preprocessor like SCSS. The symbol simply represents the component we are styling so it can be used to style the component’s CSS pseudo-elements or classes, like we did.

Animation capabilities

As it should, styled-components also supports CSS animations through the keyframes helper function. This function makes use of template literal to define the CSS of the animation and returns a value that can now be interpolated into the css helper function or used with the styled API.

Here’s a simple example from the docs that shows what the keyframes function looks like in our code:

import styled, { keyframes } from 'styled-components'

const fadeIn = keyframes`
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
`

const FadeInButton = styled.button`
  animation: 1s ${fadeIn} ease-out;
` 

Conclusion

We’ve finally covered what I believe to be the most important and frequently used parts of the styled-components library. You can learn more about the library in the comprehensive documentation here.

Get setup with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.
  3. $ npm i --save logrocket 

    // Code:

    import LogRocket from 'logrocket';
    LogRocket.init('app/id');
    Add to your HTML:

    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin
Get started now
Yusuff Faruq Frontend web developer and anime lover from Nigeria.

Leave a Reply