Skip to main content

Astro components do not merge HTML attributes

Over the past month I have been enjoying working in the Astro ecosystem recently, and have been working on some simple community packages.

I’ve learned that Astro merges component props and element attributes differently, so I’m writing this short PSA to help myself and other Astro devs in the future.

First, some terminology:

  1. Props are the data or properties that we pass to components
  2. Attributes are the data that we pass directly to HTML elements

We might think of these interchangeably in most component-based frameworks, but as we will see here they are different in Astro components.

Props: left-to-right

For props, Astro merges from left-to-right. This means that a prop on the left-side of a component call will be overwritten by the prop with the same name on the right:

<Example side="left" side="right" />
<!-- side: "right" -->

The resulting side prop within the Example component will be “right”.

The above example is a little contrived, but you can imagine a scenario where you are creating a component that allows overriding default values:

<ElementCard element="Hydrogen" {...Astro.props} />

Attributes: no merging

However, Astro does not merge attributes on HTML elements. So if you have two identical attributes passed to an element, that is what goes to the DOM. Browsers typically ignore any duplicate attributes, so the prevailing value is the first from left-to-right:

<input type="text" type="number" />
<!-- type: "text" -->

The resulting type attribute for input element will be “text”.

If you want to create a component that allows overriding default attribute values, then you need to spread them on the left-side of the element:

<input {...Astro.props} type="text" />

This behavior might be the opposite of what you expect. For a counter example, React merges both props and attributes. But whatever the reason for the difference, it’s important to remember.

tl;dr

In Astro you should spread props to components on the right, and elements on the left:

<Component default="value" {...Astro.props} />
<element {...Astro.props} default="value"></element>

You can see this behavior in action live on StackBlitz.


That’s all. Hope it was helpful to you, and memorable for me!

Happy coding!