Quick Start Guide to Attrs in Styled Components
Tags: styledcomponents, css, react
Published: 2020-05-25 13:27:31

When learning styled components, you may have noticed the use of `attrs` and be thinking:

Huh. What does this do? When would I need to use `attrs`?

The best way to explain the use case of `attrs()` in styled components is to dive right into some examples.

Ready? Let's do it.

## **Use Case 1: Defining Default Attributes**

Here I've put together a simple button styled component:

```jsx
import styled from 'styled-components';

const Button = styled.button`
  display: block;
  font-size: 1rem;
  font-weight: bold;
  color: white;
  border-radius: 4px;
  transition: 0.2s;
  cursor: pointer;
  border: none;
  padding: 1rem;

  &:hover {
    opacity: 0.7;
  }
`

export { Button };
```

And I'm going to use a couple of these styled button components in my app:

```jsx
import React from 'react';
import { Button } from 'components/common';

const App = () => {
  return (
    <>
      <h1>Buttons</h1>
      <Button>Hello there</Button>
      <Button>Wassuuuupppp</Button>   
      <Button>Click me</Button>   
    </>
  )
}

export default App;
```

In case you didn't know, the default type for HTML buttons is `type="submit"`. 

So in my above design, when a button is clicked it will result in a page reload (becuase of the default behaviour). 

But what if you wanted to change the default type to `type="button"`? 

Or set any HTML attribute as default for that matter?

Well, you could add this as a prop directly to the component like this:

```jsx
import React from 'react';
import { Button } from 'components/common';

const App = () => {
  return (
    <>
      <h1>Buttons</h1>
      <Button type="button">Hello there</Button>
      <Button type="button">Wassuuuupppp</Button>      
      <Button type="button">Click me</Button>      
    </>
  )
}

export default App;
```

However, if the attribute can be considered a default across your application, it's better to use the `attrs()` function instead and define the default there:

```jsx
import styled from 'styled-components';

const Button = styled.button.attrs(props => ({
  type: props.type || 'button'
  // Every <Button /> will now have type="button" as default
}))`
  display: block;
  font-size: 1rem;
  font-weight: bold;
  color: white;
  border-radius: 4px;
  transition: 0.2s;
  cursor: pointer;
  border: none;
  padding: 1rem;

  &:hover {
    opacity: 0.7;
  }
`

export { Button };
```

This is much more efficient than adding a prop to every component if you find yourself turning to the same atrribute over and over.

Or put another way:

> The rule of thumb is to use `attrs` when you want every instance of a styled component to have that prop, and pass props directly when every instance needs a different one
> -[Styled Components Docs](https://styled-components.com/docs/faqs#when-to-use-attrs)

This means that we can ommit the default attribute, and only pass props when we want to change the default:

```jsx
import React from 'react';
import { Button } from 'components/common';

const App = () => {
  return (
    <>
      <h1>Buttons</h1>
      <Button>Hello there</Button>
      <Button>Wassuuuupppp</Button>      
      // Add a prop to override the default defined in attr
      <Button type="submit">Click me</Button>      
    </>
  )
}
```

That is the simplest way to get started with `attrs`!

If you're looking to get more dynamic continue on to the next use case...

## **Use Case 2: Defining Dynamic Props**

Building from the previous use case, using `attrs` also allows you to attach dynamic props to a component.

Sticking with our button example from use case 1, let's add a default size of our button:

```jsx
import styled from 'styled-components';

const Button = styled.button.attrs(props => ({
  type: props.type || 'button'
}))`
  display: block;
  font-size: 1rem;
  font-weight: bold;
  color: white;
  border-radius: 4px;
  transition: 0.2s;
  cursor: pointer;
  border: none;
  /* define default margin and padding: */
  margin: 1rem;
  padding: 1rem;

  &:hover {
    opacity: 0.7;
  }
`

export { Button };
```

The above code will make the margin and padding for all buttons `1rem` by default.

We can, however, make this more dynamic. 

Let's say we want to make a larger version of the button, we could pass a size prop like this:

```jsx
const App = () => {
  return (
    <>
      <h1>Buttons</h1>
      <Button size="2rem">Hello there</Button>
      <Button size="3rem">Wassuuuupppp</Button>      
      // Add a prop to override the default defined in attr
      <Button type="submit">Click me</Button>      
    </>
  )
}
```

And then in our styled component, we can make the margin and padding dynamic:

```jsx
import styled from 'styled-components';

const Button = styled.button.attrs(props => ({
  type: props.type || 'button',
  size: props.size || '1rem'
}))`
  display: block;
  font-size: 1rem;
  font-weight: bold;
  color: white;
  border-radius: 4px;
  transition: 0.2s;
  cursor: pointer;
  border: none;
  /* pass the dynamic props: */
  margin: ${props => props.size};
  padding: ${props => props.size};  

  &:hover {
    opacity: 0.7;
  }
`

export { Button };
```

This leverages what we learned in use case 1:

We set the default size as `1rem`, but if a specific prop is passed, it overwrites the default.

With this override, we can now dynamically set the margin and padding using the passed prop.


## **Summary**

In short, the use case of `attrs()` in styled components is:
- To define default HTML attributes in your styled components to save you passing the prop
- When you want to override the default HTML attribute, pass props to a component that dynamically styles the component 


## **Download The Free Styled Components Cheat Sheet**
I'm currently working on a styled components 1-pager. [Join the waiting list](https://scalablecss.com/styled-components-cheat-sheet/) and be notified when it launches.