Properties
This lesson explains JavaScript properties in Web Components, how they differ from attributes, and how to design predictable property APIs for reusable custom elements.
What Are Web Component Properties?
Properties are JavaScript values on a custom element instance.
They are the best way to pass complex data such as objects,
arrays, functions, and booleans into a Web Component from
application code.
Attributes are declarative and string-based. Properties are
programmatic and can hold any JavaScript type. Good component
APIs use both intentionally.
| Concept | Description |
| Property | JavaScript value on the element instance |
| Attribute | String value in HTML markup |
| Getter / Setter | Controlled property access on the class |
| Best For | Objects, arrays, functions, and rich state |
| Set From | JavaScript, frameworks, and app logic |
| Main Goal | A powerful and predictable component API |
Basic Property Example
class UserBadge extends HTMLElement {
connectedCallback() {
this.render();
}
set user(value) {
this._user = value;
this.render();
}
get user() {
return this._user;
}
render() {
const name = this._user?.name || "Guest";
const role = this._user?.role || "viewer";
this.textContent = `${name} (${role})`;
}
}
customElements.define("user-badge", UserBadge);
const badge = document.querySelector("user-badge");
badge.user = {
name: "Alex Chen",
role: "admin"
};
Properties are ideal when the component needs structured data that
does not fit cleanly into HTML attributes.
Properties vs Attributes
| Feature | Property | Attribute |
| Type | Any JavaScript value | String only |
| Set in HTML | Not directly for complex values | Yes, declaratively |
| Set in JS | element.value = ... | setAttribute() |
| Best For | Objects, arrays, functions | Simple config and flags |
| SSR Friendly | Needs client hydration | Available in server HTML |
Getters and Setters
class AppButton extends HTMLElement {
get disabled() {
return this.hasAttribute("disabled");
}
set disabled(value) {
this.toggleAttribute("disabled", Boolean(value));
this.render();
}
get label() {
return this.getAttribute("label") || "";
}
set label(value) {
if (value == null) {
this.removeAttribute("label");
} else {
this.setAttribute("label", value);
}
this.render();
}
}
Getters and setters let you expose a clean property API while
keeping internal state, attributes, and rendering in sync.
Boolean Properties
class ToggleSwitch extends HTMLElement {
get checked() {
return this.hasAttribute("checked");
}
set checked(value) {
this.toggleAttribute("checked", Boolean(value));
this.updateUI();
}
updateUI() {
this.setAttribute(
"aria-checked",
String(this.checked)
);
}
}
const toggle = document.querySelector("toggle-switch");
toggle.checked = true;
Boolean properties are commonly mirrored to attributes so the
component works in both HTML and JavaScript contexts.
Object and Array Properties
class DataList extends HTMLElement {
set items(value) {
this._items = Array.isArray(value) ? value : [];
this.render();
}
get items() {
return this._items || [];
}
render() {
this.innerHTML = this.items
.map((item) => `<li>${item.label}</li>`)
.join("");
}
}
list.items = [
{ id: 1, label: "Home" },
{ id: 2, label: "Settings" }
];
Use properties for collections and structured data. Attributes
are a poor fit for JSON-like values in production APIs.
Function Properties
class AutocompleteInput extends HTMLElement {
set filterFn(fn) {
this._filterFn = typeof fn === "function" ? fn : null;
}
get filterFn() {
return this._filterFn;
}
filterItems(items, query) {
if (!this._filterFn) return items;
return this._filterFn(items, query);
}
}
Function properties are useful for advanced customization, but
they should be documented clearly because they cannot be set from
plain HTML attributes.
Reflecting Properties to Attributes
class StatusBadge extends HTMLElement {
static get observedAttributes() {
return ["variant"];
}
get variant() {
return this.getAttribute("variant") || "info";
}
set variant(value) {
if (value) {
this.setAttribute("variant", value);
} else {
this.removeAttribute("variant");
}
}
attributeChangedCallback() {
this.render();
}
}
Reflect important property values to attributes when you want CSS
styling, SSR, or HTML-based configuration to stay in sync.
Using Properties in Frameworks
// React
<user-card user={{ name: "Alex", role: "admin" }} />
// Vue
// <user-card :user="currentUser" />
// Plain JavaScript
const card = document.querySelector("user-card");
card.user = currentUser;
Frameworks usually set properties on custom elements rather than
serializing complex values into attributes. Design your API with
that in mind.
Default Property Values
class PaginatedList extends HTMLElement {
constructor() {
super();
this._page = 1;
this._pageSize = 10;
this._items = [];
}
get page() {
return this._page;
}
set page(value) {
this._page = Number(value) || 1;
this.render();
}
}
Initialize default values in the constructor so the component
behaves predictably before any property is set by the consumer.
Common Property Patterns
| Pattern | Use Case | Example |
| Reflected boolean | Disabled, open, checked states | element.open = true |
| Object property | User, config, record data | element.user = { ... } |
| Array property | Lists, options, menu items | element.items = [] |
| Callback property | Custom render or filter logic | element.filterFn = fn |
When to Use Properties
- You need to pass objects, arrays, or functions into a component.
- Framework apps set component state programmatically.
- You want a rich JavaScript API beyond HTML attributes.
- Boolean or numeric values are controlled from app logic.
- Internal rendering depends on structured data.
- You are designing a reusable library for multiple codebases.
Common Property Use Cases
- User, product, and record objects in display components.
- Item lists in menus, tabs, and data tables.
- Open, disabled, and checked states in interactive widgets.
- Configuration objects for charts, maps, and editors.
- Callback hooks for custom filtering or formatting.
- Framework-driven state in React, Vue, and Angular apps.
Property Best Practices
- Use properties for rich JavaScript values and attributes for simple config.
- Expose getters and setters for important public properties.
- Initialize defaults in the constructor.
- Reflect key values to attributes when HTML or CSS needs them.
- Validate and normalize values inside setters.
- Document property types, defaults, and sync behavior.
- Trigger rendering or state updates from setter changes.
Common Property Mistakes
- Using attributes for objects and arrays.
- Exposing public state only as internal fields without getters/setters.
- Not updating the UI when a property changes.
- Assuming HTML attributes automatically become typed properties.
- Forgetting default values for unset properties.
- Reflecting every property to attributes unnecessarily.
- Creating property names that do not match framework conventions.
Key Takeaways
- Properties are the JavaScript API of a Web Component.
- Use them for objects, arrays, booleans, and functions.
- Getters and setters help keep state, attributes, and UI in sync.
- Frameworks prefer properties for complex component inputs.
- Good property design makes custom elements easier to reuse.
Pro Tip
Design your public API in two layers: simple values through
attributes, rich app state through properties. That gives you
both declarative HTML usage and powerful framework integration.