Measuring React styled-components performance & best practices
Tags: react, tutorial, html, css
Published: 2022-05-05 21:56:46

Nothing feels worse to a new user than having to navigate across a slow-performing webapp. Nine out of ten times, I hit 'back' on a webpage once I realize a page is rendering excruciatingly slow. Webapp performance is one of the most important components of user experience, and that is why search engines take into account website metrics such as 'first contentful paint' or 'time to interactive' when ranking.

![Lighthouse metrics](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6xk4k0vzkr56g4ataa6x.png)
*Lighthouse metrics for an example webpage*

I'll assume you already have some working knowledge on what styled-components is and how it works. But just in case, styled-components is one of the most popular open-source styling libraries, especially in the React ecosystem. Instead of applying styles to an entire webpage or specific HTML elements using the class/id system, you can apply styles onto individual React components. One of my favorite aspects of the styled-components approach is it neatly blends together logic & styling – JS, HTML, and CSS – so everything is accessible from just one file.

```js
const StyledButton = styled.button`
  font-size: 14px;
  color: #525252;
  background-color: #7AEFB2;
  border-color: transparent;
`

const ButtonComponent = ({ isLoading, onClick, children }) => {
  return (
    <StyledButton className=”example-btn” onClick={onClick}>
      {children}
      {isLoading ? <LoadingSpinner /> : null}
    </StyledButton>
  )
}

export default ButtonComponent
```
*Example styled component button in React*

Now that we've covered the background, let's ask the important questions. How can we gauge how well our styled-components are performing? What metrics should we look out for? And what are some best practices we can implement to keep our code efficient? 

## Measuring performance

We'll be using Chrome DevTools' Performance Monitor to gauge a page's performance in real time. It will appear like this:

![Performance monitor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zuicp2bpgq5d0f7wx58i.png)
*Chome DevTools performance monitor*

Navigate to the page containing your code, open up the performance monitor, press record, perform an action, and stop recording. You'll see something like this:

![Performance timeline & summary](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4vses2mu3r02eedn0v3r.png)
*Performance timeline & summary*

Looking at the summary, we can see scripting takes up the majority of the recording time – 1904ms out of 2880ms. It appears we can make the most significant improvements in this department. Let's dive further by clicking into the 'Bottom-up' tab.

![Performance bottom-up tab](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqz5itjs3w7a79ps514h.png)
*Performance Bottom-up tab*

The 'insertBefore' scripting activity takes 364.4ms – the longest of any process. Let's figure out where this code comes from.

![Performance bottom-up tab details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/87pjhe7k679q5z8gdk2f.png)
*`insertBefore` subfolders*

The code with the greatest 'Self time' comes from styled-components. Now that we've identified where the problem lies, let's fix it by making our code more efficient.

*To learn more about using the DevTools performance monitor, check out [this blog post about optimizing render performance](https://www.useanvil.com/blog/engineering/improve-optimize-react-render-performance-part-1/)!*

## Best practices

The first step in code optimization is to examine the way our code is structured. Let's take a look at some of those best practices for styled-components.

### Dynamic styling

Oftentimes we want the styling of a UI component to be dependent upon some logic or state in the application. For example, we may want the background of a div to be gray when being hovered over. We can achieve this by applying dynamic styling.

```js
const Field = styled.div`
  background: ${props => props.isHover ? '#E2EEF0' : '#FFFFFF'};
`
```
*Example of dynamic styling - div with light cyan background upon hover*

What if we want multiple dynamic styles to be applied? It could look quite repetitive.

```js
const Field = styled.div`
  color: ${props => props.isSelected ? '#2D2D2D' : '#7A7A7A'};
  border-radius: ${props => props.isSelected ? '4px' : '0px'};
  background: ${props => props.isHover ? '#E2EEF0' : '#FFFFFF'};
`
```
*Multiple dynamic styles - the 'meh' way*

Let's clean up our code by importing the props once for each prop instead of doing it on a per-line basis.

```js
const Field = styled.div`
  color: #7A7A7A;
  border-radius: 0px;
  background: #FFFFFF;

  ${({ isSelected }) => isSelected && `
    color: #2D2D2D;
    border-radius: 4px;
  `}

  ${({ isHover }) => isHover && `
    background: #E2EEF0;
  `}
`
```
*Multiple dynamic styles - the okay way*

Having a bunch of dynamic styles can quickly get complicated. Let's imagine we have a styled-component that takes a 'displayStyle' prop that applies various combinations of CSS. Like so:

```js
const StyledInput = styled.input`
  font-size: 14px;
  border-radius: 2px;

  ${({  displayStyle }) => displayStyle === 'compact' && `
    border-top: none;
    border-left: none;
    border-right: none;
    padding-top: 0;
    padding-left: 0;
    padding-right: 0;
    margin-left: 0;
    margin-right: 0;
    font-size: 12px;
    box-shadow: none;
  `}

  ${({ displayStyle }) => displayStyle === 'internal' && `
    border: none;
    margin-left: 0;
    margin-right: 0;
    font-weight: bold;
  `}

  ${({ displayStyle }) => displayStyle === 'large' && `
    border: 2px;
    margin-left: 10px;
    margin-right: 10px;
    font-size: 22px;
  `}
  …
`
```
*Another example of multiple dynamic styles - the okay way*

It can get quite confusing to track all the CSS rules when there are a bunch of different display styles. We can compartmentalize everything by creating distinct styled-components for each display style.

```js
const StyledInput = styled.input`
  font-size: 14px;
  border-radius: 2px;
`

const CompactInput = styled(StyledInput)`
  border-top: none;
  border-left: none;
  border-right: none;
  padding-top: 0;
  padding-left: 0;
  padding-right: 0;
  margin-left: 0;
  margin-right: 0;
  font-size: 12px;
  box-shadow: none;
`

const InternalInput = styled(StyledInput)`
  border: none;
  margin-left: 0;
  margin-right: 0;
  font-weight: bold;
`

const LargeInput = styled(StyledInput)`
  border: 2px;
  margin-left: 10px;
  margin-right: 10px;
  font-size: 22px;
`

export default function Input ({ displayStyle, …props }) {
  let InputComponent = StyledInput
  if (displayStyle === 'compact') InputComponent = CompactInput
  else if (displayStyle === 'internal') InputComponent = InternalInput
  else if (displayStyle === 'large') InputComponent = LargeInput

  return (
    <InputComponent {...props} />
  )
}
```
*Multiple dynamic styles - the cleanest way*

By adopting this improved format of structuring your styled-components, I hope you'll see some improvement in performance.

### Global styles

Styled-components has a helper function named [`createGlobalStyle`](https://styled-components.com/docs/api#createglobalstyle) which generates a special component that handles global styles. The function works by creating a HTML style tag. Whenever a React component with `createGlobalStyle` is mounted, `createGlobalStyle` is called and a new style tag is generated. Using the helper function with a React component that is mounted & unmounted frequently will lead to redundant style tags in the DOM, so it is best to minimize the number of times the function is used.

```js
const DropdownGlobalStyle = createGlobalStyle`
  .selected-option {
    background-color: #3E3E57;
  }
`

function Dropdown (props) {
  return (
    <>
      …
      <DropdownGlobalStyle />
    </>
  )
}

const InputGlobalStyle = createGlobalStyle`
  .error-container {
    color: #FB7578;
  }
`

function Input (props) {
  return (
    <>
      …
      <InputGlobalStyle />
    </>
  )
}
```
*Using `createGlobalStyle` for multiple components - the bad way*

Let's create global styles once only in the App component.

```js
const AppGlobalStyle = createGlobalStyle`
  .selected-option {
    background-color: #3E3E57;
  }

  .error-container {
    color: #FB7578;
  }
`

function App () {
  return (
    <>
      …
      <AppGlobalStyle />
    </>
  )
}
```
*Using `createGlobalStyle` once in the root component - the better way*

## Summary

We've covered how to measure the performance of your styled-components and best practices on structuring your code. By implementing these techniques into your development process, you can worry less about having a slow-performing web app!

We've applied these practices to our code at Anvil, and believe sharing our experience helps everyone in creating awesome products. If you're developing something cool with PDFs or paperwork automation, let us know at [developers@useanvil.com](mailto:developers@useanvil.com). We'd love to hear from you.