DEV Community

GaryBarnes17
GaryBarnes17

Posted on • Edited on

Enhancing your Developer Experience with Styled-Components

Intro

Hey everyone, I’m here today to talk about styled-components. If you have never heard of or used styled-components before, that’s great, this talk is for you. If you have used styled-components before, that’s also great, this talk is still for you. There’s always more to learn, and my goal with this talk is for each of you to leave here with the confidence to build and innovate with styled-components.

I am going to make a few assumptions about those of you that are attending this talk. The first is that you are reasonably comfortable with the building blocks of the web - HTML, CSS, and JavaScript. The next is that you have experience with a framework for front end development, specifically React, since styled-components is built for React. However, I am not going to assume you have worked with Typescript in-depth before, nor will I assume that you have worked with the Asurion UI component library, the team that I work on.

Overview

The styled-components library seeks to answer the question - “How can I style my React components?”. When we style our React components with vanilla CSS, we generally do so by giving our components an ID or class name, and then applying styles via class name and/or ID selectors. On the other hand, Styled Components answers this styling question by attaching styles to components directly, and creating new components in the process.

As we go through this talk, I want to focus on two key characteristics of React, which are highlighted in purple in the following diagram:

  1. Decomposing web applications into independent, reusable chunks of logic and markdown called Components
  2. Adapting the behavior of these components based on props

Image description

With this first characteristic in mind, we can begin to discuss styled-components.

Map Styles Directly to Components

One of the key value propositions of styled-components is that it allows you to completely remove the mappings between your styles and your React components. When we are working in the React world, we like to decompose a page into components. This paradigm is useful because it allows us to break up logic and markdown into reusable units.

Styled-components allows us to extend this paradigm to our styling as well. Meaning, instead of creating styles with CSS and then assigning these to a particular component, group of components, or group of elements with the various selectors we can use, we wrap each component in its own particular set of styles. If we want to reuse some styles, we can simply reuse the component that these styles wrap. Here is a visual representation of this. On the left, we see that it is possible to give multiple different components the same class name, or have multiple class names (or any other CSS selector) apply to a single component. Styled-components on the other hand ensures a 1:1 mapping of styles to components. Again, on the left we see that styles are applied to an existing component, whereas on the right, styles wrap a component and in turn, create a new component with styles attached.

Image description

General Syntax

A simple example is useful here (Code Sandbox). As we go through this and the rest of the examples, please feel free to post any questions in the chat and I will do my best to address them. In this case, we are rendering two sections with the text “Hello World”. One is styled with styled-components, and the other is styled with vanilla CSS. Let me explain briefly what is going on with the styled-components syntax. Consider the title component. Put simply, this chunk of code applies styles to an h1 element. In this case, we generate a Title component by attaching styles to this h1, and then can use this as regular React component. We do the same for the Wrapper component, which is a section element with some styles attached. Compare this to the second example, where we must attach styles via a className selector (of course, we could go about selecting these elements many other ways, such as by element type or by ID). As you can see, the syntax for styled-components is generally of of the following three expressions:

const StyledComponent = styled.element`
    css-property: value
`

const StyledComponent = styled("element")`
    css-property: value
`

const StyledComponent = styled(Component)`
    css-property: value
`
Enter fullscreen mode Exit fullscreen mode

In the most basic example, we attach some styles to an element. This first expression in which we do this is shorthand for the second expression. Regardless of which expression we use, the styled function returns a component with styles attached to it. As you can see in the third expression, you can use the styled method with any custom component (including one from Asurion UI), as long as this component attaches the passed className prop to some element on the DOM. The following snippet is from the Asurion UI Text component, which accepts a className prop and consequently can be styled with styled-components.

// From the Asurion UI Text Component

export const Text = forwardRef<HTMLSpanElement, TextProps>(
  ({ as, size, weight, className, ...rest }, fRef) => (
    <InternalProvider>
      <TextElement
        className={className}
        fontSize={size}
        fontWeight={weight}
        ref={fRef}
        as={as}
        {...rest}
      />
    </InternalProvider>
  )
);
Enter fullscreen mode Exit fullscreen mode

Adapting Styles Based on Props

As I mentioned before, the second characteristic of React that is relevant for this talk is that we often adapt the behavior of components based on the props that are passed into them. This allows us to change how our reusable chunks of code behave based on how the user interacts with them and the current state of the web application. This leads us into another key value proposition of styled-components, which is that it allows us to adapt the style of components based on props. Put simply, a styled-component behaves like a regular React component in that it can accept any number of arbitrary props. When using vanilla CSS, adapting styles of components based on props is tricker and involves conditionally applying a class name, which is harder to read and adds a lot of mental overhead. Consider the following example. I included a snippet here, but have also built out this example in CodeSandbox (Code Sandbox):

const Button = styled.button`
  /* Adapt the colors based on primary prop */
  background: ${props => props.primary ? "palevioletred" : "white"};
  color: ${props => props.primary ? "white" : "palevioletred"};

  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

render(
  <div>
    <Button>Normal</Button>
    <Button primary>Primary</Button>
  </div>
);
Enter fullscreen mode Exit fullscreen mode

In this case, our goal is to create a button with a primary and non-primary variant, which is handled by passing in a primary prop. To keep some consistency between the two examples, I created a Button component that handles this stying with CSS, and a StyledButton component that handles the styling entirely with styled-components. With styled-components, we can keep these styles in one place, and adapt the styles we render in the same way we would adapt the markdown we render in a React Component based on props. On the other hand, if we use vanilla CSS, we must use a ternary operator to conditionally apply a class name based on props. This style must be defined in a separate file, and the inability to adapt a subset of the styles based on props results in duplicated styles. As you can see, this results in higher complexity and greater mental overhead.

What is Styled-Components?

Note: The following diagram and this topic in general is heavily inspired by the info in this video starting at 27:56

Before we dive into more complex examples, I think it is important to establish what exactly styled-components is, and when it should be used. When talking about style libraries, we can generally group libraries into three categories: extensions to CSS, behavior libraries, and style systems. It is important to note that these categories are not mutually exclusive.

Extensions to CSS include solutions such as Tailwind CSS, SASS, and styled-components. These libraries either expand on the syntax of CSS, such as SASS, or let us express CSS styles with a different syntax, such as Tailwind CSS (which uses utility classes) and styled-components, which uses CSS-in-JS, which is basically when you write CSS styles inline with other Javascript code. (Thorough explanation here).

Behavior libraries refer to libraries that seek to ensure components are accessible and behave appropriately. These libraries do not address styling concerns. Examples include React-Aria and Headless-UI.

Style systems refer to libraries that have built-in styles that are exposed to users, and generally restrict users to styling their components within a certain bounds. Examples include Bootstrap and Tailwind UI.

Of course, these groups are not mutually exclusive. For example, styled-components actually allows developers to use a SASS-like syntax via the stylis pre-processor. Additionally, what we refer to as “Component Libraries” generally occupy the space between style systems and behavior libraries. As in, they limit users to styling components within a certain bounds, and they seek to ensure these components behave correctly and are accessible. These component libraries can be built on an extension to CSS, such as styled-components or Tailwind. In fact, it is generally preferable to build these component libraries with one of these extensions. A perfect example here would be our own Asurion UI, which is our component library that is built on top of styled-components.

Image description

Why use Styled-Components?

Now that we have established where styled-components fits in regards to other styling solutions, we can begin to talk about why styled-components is my preferred tool for working with styles in React, and why it should be yours too. As I mentioned before, I feel that the biggest advantage to styled-components is that it allows developers to think of styles in the same way they think of components. This is done by effectively making each set of styles a component itself, and by allowing us to conditionally apply certain styling rules based on props.

Another big advantage of using styled components is that it allows developers to decompose a component into independent chunks of style and logic, which makes code much more readable. As you can see with this diagram, we are able to completely separate the logic, structure, and styling of each component. At the bottom of the diagram we have the logic and structure of the component, this handles things like state and lifecycle events. At the top of the diagram we have the styles of the component, which handles, well, styling. The advantage here is that if we have an issue with the styling or logic of a component, we know exactly where to look and reduce the risk of making a change to one that accidentally affects the other.

Image description

On top of these developer experience enhancements, styled-components also offers many performance benefits. These include automatic generation of unique class names to prevent overlap or duplication, automatic loading of only the CSS that each page needs, and automatic deletion of unused CSS.

Conclusion

In conclusion, styled-components is a styling library that acts as an extension to CSS by allowing us to express CSS styling rules in Javascript code. Styled-components aligns with React’s core principles by removing the mapping between styles and components and adapting styles based on props, which results in less mental overhead. Styled-components is not an alternative to a component library, but rather, can be used to build one and/or to add styles on top of one. On top of this, one of the most valuable features of styled-components is that it allows us to decompose components into chunks of logic and style. For this reason, it is my preferred styling solution. However, it is important to note it is far from the only solution, I think Tailwind also does a phenomenal job of this decomposition, even if it does fall short in other areas.

Now, let’s move on to a hands-on example...

Top comments (0)