Quick Start Guide to Attrs in Styled Components

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:

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:

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:

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:

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

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

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:

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:

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:

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 and be notified when it launches.

Learn Something New Everyday,
Connect With The Best Developers!

Sign Up Now!

& 500k+ others use Hashnode actively.

No Comments Yet