The Difference Between HTML Attributes and Properties
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)
So attributes and properties do the same thing, right?
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.
HTML vs. JavaScript
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 
setAttributemodifies the HTML code in the DOM. CallinggetAttributereads the attribute from the HTML code in the DOM. - When you use 
myInput.valueyou’re accessing a property of the JavaScript object representing your input. 
When do attributes and properties stay in sync?
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.
<input> element’s value stay in sync?
    
      
        Why doesn’t the <input> element’s value stay in sync?
      
    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
valueHTML attribute represents the default value of an<input>element, and the corresponding property isdefaultValue. Thevalueproperty 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.
Do attributes always have a corresponding property?
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 
classattribute gets renamed toclassNamewhen used as a property. data-attributes get stored in a specialdatasetproperty.
Do properties always have a corresponding attribute?
No, sometimes properties don’t have corresponding attributes. For example, HTML elements have an innerHTML property but no innerHTML attribute.
Properties can contain non-string data
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.
What’s the deal with boolean attributes?
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.
Which should you use? An attribute or a property?
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.
Wrapping up
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.
Related reading
Here are a few resources that helped me understand how this all works:
