Back to Styled Components

Attaching Additional Props

sections/basics/attaching-additional-props.mdx

latest2.5 KB
Original Source

Attaching additional props | v2

To avoid unnecessary wrappers that just pass on some props to the rendered component or element, you can use the .attrs constructor. It allows you to attach additional props (or "attributes") to a component.

This way you can for example attach static props to an element or pass a third-party prop like activeClassName to React Router's Link component. Furthermore, you can also attach more dynamic props to a component. The .attrs object also takes functions, that receive the props that the component receives. The return value will be merged into the resulting props as well.

Here we render an Input component and attach some dynamic and static attributes to it:

react
const Input = styled.input.attrs<{ $size?: string; }>(props => ({
  // we can define static props
  type: "text",

  // or we can define dynamic ones
  $size: props.$size || "1em",
}))`
  color: #BF4F74;
  font-size: 1em;
  border: 2px solid #BF4F74;
  border-radius: 3px;

  /* here we use the dynamically computed prop */
  margin: ${props => props.$size};
  padding: ${props => props.$size};
`;

render(
  <div>
    <Input placeholder="A small text input" />
    

    <Input placeholder="A bigger text input" $size="2em" />
  </div>
);

As you can see, we get access to our newly created props in the interpolations, and the type attribute is passed down to the element.

Overriding .attrs

Notice that when wrapping styled components, .attrs are applied from the innermost styled component to the outermost styled component.

This allows each wrapper to override nested uses of .attrs, similarly to how CSS properties defined later in a stylesheet override previous declarations.

Input's .attrs are applied first, and then PasswordInput's .attrs:

react
const Input = styled.input.attrs<{ $size?: string }>((props) => ({
	type: 'text',
	$size: props.$size || '1em',
}))`
	border: 2px solid #bf4f74;
	margin: ${(props) => props.$size};
	padding: ${(props) => props.$size};
`;

// Input's attrs will be applied first, and then this attrs obj
const PasswordInput = styled(Input).attrs({
  type: "password",
})`
  // similarly, border will override Input's border
  border: 2px solid aqua;
`;

render(
  <div>
    <Input placeholder="A bigger text input" $size="2em" />
    

    <PasswordInput placeholder="A bigger password input" $size="2em" />
  </div>
);

This is why PasswordInput is of a 'password' type, but still uses the $size attribute from Input.