Quick Start Guide to Attrs in Styled Components

May 25, 2020

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 (because of the default behavior).

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 attribute 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 omit 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

Want to master Styled Components in React?

Levelling up your React and CSS skills in 2021 is a great idea. Here's why:

  • Increase your employability and value as a React developer
  • Learn how to style your React apps in a clean, efficient method
  • Get a deep understanding of a CSS-in-JS library
Scalable CSS LogoView recommended course on Udemy
Tom Ray

By Tom Ray, a front-end developer who lives in London with his fiancée and cat Arnold.

© Scalable CSS 2021, built with ❤️ in London, UK.