Element Attributes Are Not Element Properties

Published 2025-04-30, About 6 minute read.

Something that has come up a number of times as people are stepping away from their javascript frameworks and leaping into web components is that they don't know the difference between attributes and properties. This matters because there's a big difference between the two, and you need to know when to use either or both with your custom elements.

Let's jump right in.

Element Attributes

An attribute are those things in html that allow us to attach key-value information to elements:

These are in HTML, XML, and XHTML. In all those cases, the values are only ever strings.

The one exception to this is in HTML where you can have an attribute without a value, in which the value is just an empty string "" if you were to retrieve it in javascript. These are generally considered for boolean states like disabled or hidden.

Element Properties

Properties have a special meaning for Elements. When you inspect the DOM in the browser, and inspect a particular node, you have an Element in hand. That Element is an object (in particular, an Element instance object) and can have its own properties.

Take a look- if you select that custom element in the DOM, it will have that property. You may not see it right away, because if you log the node or use $0 in Chrome it will not print the object, but the node representation:

So, properties are plain ol' javascript properties, just in this case we more concisely call this a field becuase it's defined on the class explicitly.

Why the confusion?

I have opinions about how we came to this mess where people confuse the two. I had to spend a while diving into attributes and properties while first learning Lit. I'm convinced it has to do with how frameworks use templates.

Take React. They famously switched over to JSX to include templating inside the actual javascript files. As best I can tell and remember this happened around 2013-2014.

Despite React saying explicitly in its introductory explanation of JSX that JSX is not HTML, most people took for granted how important this distinction was, and continue to do so. JSX is an abstraction that represents components and elements using XML-like syntax. Because you can freely call components or regular web elements in the XML you write, this produced a whole generation of developers that didn't need to distinguish between the abstraction and the platform API for DOM.

I'm pretty certain that as people got used to seeing this:

They forgot completely about the JSX pragma and desugaring that was happening under the hood:

JSX is a shorthand for creating a representation of a DOM tree. It is not HTML or a DOM tree itself.

The important thing here is that attribute above is actually a property and not an attribute, even though it looks like an attribtue in JSX. And what confuses things further is that that property could be a valid attribute eventually when the element is rendered by React if React recognizes that property is actually a valid HTML attribute.

This is not specific to React, though, because in every other javascript framework that has declarative templates you have the same issue. The template is a representation of the DOM tree you want the framework to eventually render, and so bindings could be either: a property at runtime that never translates into an attribute in the DOM or a property that is recognized as a valid HTML attribute and eventually placed in the DOM as such.

So why are they conflated in Lit?

I think this is easily seen in how decorators are used to automatically set up attribute bindings in the background. In Lit, you can add a decorator, and magically the field you're defining on this class has an attribute automatically set up for that particular property on the custom element

Under the hood, Lit makes this.myProperty reactive to changes if set directly. It also deduces a corresponding attribute (in this case myproperty because it doesn't try to guess what the kebab casing would be) and sets up reactivity so that if the attribute changes in the DOM, the property changes as well.

This is a great convenience for users of Lit. But it also can mislead people to think that there is an inherent binding between properties on a class and the attributes in the DOM. Of course there isn't a binding like this automatically. Lit just does it for you.

To be fair, Lit also is very explicit about the distinction between attributes and properties. In lit-html template bindings you have to specify when you're passing a property binding or an attribue:

But again this is not HTML! This is the template language abstraction for elements in lit-html In HTML you must use attributes with plain strings or use good 'ol scripts:

If you want the definitive guide of HTML attributes vs DOM properties, check this article by Jake Archibald out. It is exhaustive, and an excellent dive into details

My Law for Learning

I have an aphorism of sorts I like to live by, and I think it's sound advice for anyone learning and developing their craft in coding:

This is not "grumpy ol' neckbeard who's upset that kids these days can't do vanilla javascript" advice. This is "I've learned over time that this helps me be a better developer" advice. Here's what working with vanilla javascript can unlock if you work in a framework:

Find me on Bluesky or Mastodon. I also have an RSS feed here
⬅️ Previous Post
Back to top