Skip to main content

The Difference Between HTML Attributes and Properties

By Paul Hebert

Published on January 6th, 2025

Topics

Attributes and properties allow you to control how HTML elements function and read data about their state. Although the terms are often used interchangeably, subtle differences between the two can lead to unexpected behavior and bugs.

For example, when using the native HTML <input> element, there are three different ways to set its value.

1. You can set its value directly in your HTML using an attribute:

<input value="Pesto" />
Code language: HTML, XML (xml)

2. You can use JavaScript to get and set the value attribute:

const input = document.querySelector('input');
input.setAttribute('value', 'Marinara');
console.log(input.getAttribute('value')); // Logs "Marinara"
Code language: JavaScript (javascript)

3. Finally, you can use JavaScript to set and get the value property:

const input = document.querySelector('input');
input.value = 'Alfredo';
console.log(input.value); // Logs "Alfredo"
Code language: JavaScript (javascript)

Well, kinda. Here’s where things start to get weird…

Imagine we have a form with the <input> element from above:

<input value="Pesto" />
Code language: HTML, XML (xml)

A user comes along and decides to change the input’s value. They delete the word “Pesto” and replace it with “Bolognese.” Let’s see what happened to our attributes and values:

const input = document.querySelector('input');
console.log(input.value);                // Logs "Bolognese"
console.log(input.getAttribute('value'); // Logs "Pesto"
Code language: JavaScript (javascript)

Wait, what? Why is the attribute still “Pesto”!?

You can play with this behavior below. Try typing in the input and see how the attribute and property respond:

Notice how the attribute never changes while the property responds to your typing. What’s going on?

Note: When you submit a form with an input, the value property will be used, not the attribute.

When a web page is loaded in the browser, there are two different representations of the DOM:

  • HTML: The page is built of HTML elements written using the HTML syntax. Browsers know how to read this syntax and build a page. HTML elements have attributes.
  • JavaScript: But when you interact with an HTML element using JavaScript, you’re using the HTML DOM API. This means you’re actually interacting with a JavaScript reconstruction of the same HTML page. JavaScript elements are objects, so they have properties.

Under the hood, the HTML DOM API creates a JavaScript object for each element and converts its attributes to properties. When the page loads, these two representations are in sync, but they can drift out of sync.

Try running console.log(document.createElement('input'));in your console. You’ll see an object with a long list of properties, including value (as well as a property called attributes.)

Here’s my over-simplified understanding of how this works:

  • When you set the value directly in your HTML code, you’re setting an attribute. Calling setAttribute modifies the HTML code in the DOM. Calling getAttribute reads the attribute from the HTML code in the DOM.
  • When you use myInput.value you’re accessing a property of the JavaScript object representing your input.

One thing that’s confused me for a long time is that sometimes, updating an attribute also updates the corresponding property (and vice versa), but sometimes it does not!

Most attributes and properties stay in sync: Updating an element’s id attribute will update its id property and vice versa. But some attributes and properties are special. As we saw above, the <input> element’s value is one of these special cases.

The logic for when the <input> element’s value gets synced is weird. Here’s what I found in my testing:

  • Updating the attribute…
    • Updates the property (Unless the user has already typed in the input.)
    • Updates the visually displayed value (Unless the user has already typed in the input.)
  • Updating the property…
    • Never updates the attribute.
    • Always updates the value displayed in the browser.

I was very confused about this behavior. Thankfully, Valtteri Laitinen commented on this post with an explanation of what’s going on under the hood:

The value HTML attribute represents the default value of an <input> element, and the corresponding property is defaultValue. The value property represents the current value of a form element and has no corresponding HTML attribute.

Honestly, I still find this behavior very odd and confusing. This excellent article by Jake Archibald goes into greater detail and helped me understand how this works: HTML attributes vs DOM properties.

In practice, different attributes and properties work differently depending on how their specifications are written. There are a few more things to keep in mind when understanding these differences.

As far as I can tell, all of the officially documented attributes have matching properties. (That said, there are a ton of different elements and attributes, so if you’re aware of one that doesn’t, please let me know!)

However, you can also add your own custom attributes to HTML elements: <input custom-attribute="" />If you add a custom attribute, it will not have a corresponding property.

There are a few other special cases to be aware of:

  • Property names are always written in camel case. (e.g. formAction). Attributes are case-insensitive (formaction === FORMACTION === fOrMaCtIOn)
  • The class attribute gets renamed to className when used as a property.
  • data- attributes get stored in a special dataset property.

No, sometimes properties don’t have corresponding attributes. For example, HTML elements have an innerHTML property but no innerHTML attribute.

Another difference between attributes and properties is that attributes are always strings while properties can contain other data types (like numbers, objects, boolean values, etc.)

For example, the <input> element’s maxLength property accepts a number. If you set the maxlength attribute in HTML it will be set as a string (<input maxlength="5">.) The browser will automatically convert it to a number when syncing it to the maxLength property.

Some attributes are known as “boolean attributes.” These attributes are either present or not present and represent either “true” or “false.” The checked attribute is boolean. If it is present at all, it is treated as true. The browser treats these all the same and renders a checked checkbox for each one:

<input type="checkbox" checked />
<input type="checkbox" checked="" />
<input type="checkbox" checked="true" />
<input type="checkbox" checked="false" />
<input type="checkbox" checked="maybe kinda sorta" />
Code language: HTML, XML (xml)

To “turn off” a boolean attribute, it needs to be removed from the element completely by using the <input> element’s removeAttribute() or toggleAttribute() methods.

There are lots of edge cases that may lead you to use one over the other, but here’s my rule of thumb:

  • When writing HTML, it’s best to use attributes. (After all, you can’t use properties in HTML!)
  • When writing JavaScript, it’s best to use properties. This tends to be less verbose and easier to read.

But this isn’t always true. Sometimes, it makes sense to break the rules.

Whew, I never thought I’d write so many words about attributes and properties! I realized several times while writing this article that I was still misunderstanding one or more aspects of how they worked, but I think I’ve finally gotten it. If you see something I got wrong, please let me know, and I’ll update this post!

Stay tuned for a follow-up article where I’ll break down the best practices for handling attributes and properties when writing custom web components.

Here are a few resources that helped me understand how this all works:

Comments

Valtteri Laitinen said:

The value HTML attribute represents the default value of an <input> element, and the corresponding property is defaultValue. The value property represents the current value of a form element and has no corresponding HTML attribute.