Skip to content

Attributes

This lesson explains Attributes with clear examples, use cases, and best practices so you can build reusable Web Components confidently.

Web Components Attributes Overview

Attributes are HTML-based configuration values used to control the appearance, behavior, state, and accessibility of Web Components. They are written directly on the custom element in markup and are always stored as strings in the DOM.

Web Components commonly use attributes for labels, variants, sizes, disabled states, selected states, open states, themes, IDs, ARIA values, and other simple public configuration.

Web Components Attributes List

  • 1. Use attributes for simple HTML configuration.
    Attributes make a custom element easy to configure directly in markup.
    <my-alert
      type="success"
      message="Saved successfully">
    </my-alert>
  • 2. Remember that attributes are strings.
    Even when an attribute looks like a number or boolean, the browser stores it as a string.
    const counter =
      document.querySelector("my-counter");
    
    console.log(
      counter.getAttribute("count")
    );
    
    // "5"
  • 3. Read attributes with getAttribute().
    Use getAttribute() to read string values from the custom element.
    const message =
      this.getAttribute("message") || "";
    
    const type =
      this.getAttribute("type") || "info";
  • 4. Set attributes with setAttribute().
    Use setAttribute() to update an attribute from JavaScript.
    this.setAttribute(
      "type",
      "warning"
    );
    
    this.setAttribute(
      "message",
      "Please check your input."
    );
  • 5. Remove attributes with removeAttribute().
    Removing an attribute is commonly used for boolean states like disabled, open, selected, and hidden.
    this.removeAttribute("disabled");
    
    this.removeAttribute("open");
  • 6. Check attributes with hasAttribute().
    Boolean attributes should usually be checked by presence.
    const isDisabled =
      this.hasAttribute("disabled");
    
    const isOpen =
      this.hasAttribute("open");
  • 7. Use boolean attributes by presence.
    A boolean attribute is true when present and false when absent.
    <my-button disabled>
      Save
    </my-button>
    get disabled() {
      return this.hasAttribute("disabled");
    }
  • 8. Do not use disabled="false" as false.
    Boolean attributes are true when present, even if the value is "false".
    <my-button disabled="false">
      Save
    </my-button>

    In this example, disabled is still considered present.

  • 9. Convert numeric attributes manually.
    Use Number(), parseInt(), or validation logic when reading numeric attributes.
    get count() {
      return Number(
        this.getAttribute("count") || 0
      );
    }
    
    get maxCount() {
      return Number(
        this.getAttribute("max-count") || 10
      );
    }
  • 10. Validate attribute values.
    Restrict public attributes such as variant, size, and type to known values.
    get variant() {
      const allowed =
        ["primary", "secondary", "danger"];
    
      const value =
        this.getAttribute("variant");
    
      return allowed.includes(value)
        ? value
        : "primary";
    }
  • 11. Use kebab-case attribute names.
    HTML attributes should be lowercase or kebab-case. JavaScript properties can use camelCase.
    <my-counter
      max-count="10"
      selected-index="0">
    </my-counter>
  • 12. Map attributes to properties.
    Use getters and setters to provide a JavaScript-friendly API.
    get selectedIndex() {
      return Number(
        this.getAttribute("selected-index") || 0
      );
    }
    
    set selectedIndex(value) {
      this.setAttribute(
        "selected-index",
        String(value)
      );
    }
  • 13. Observe important attributes.
    A custom element reacts to attribute changes only when the attribute is listed in observedAttributes.
    static get observedAttributes() {
      return [
        "type",
        "message",
        "disabled",
        "open"
      ];
    }
  • 14. Use attributeChangedCallback().
    This lifecycle method runs when an observed attribute is added, changed, or removed.
    attributeChangedCallback(
      name,
      oldValue,
      newValue
    ) {
      if (oldValue === newValue) {
        return;
      }
    
      this.update();
    }
  • 15. Handle removed attributes.
    When an attribute is removed, newValue becomes null.
    attributeChangedCallback(name, oldValue, newValue) {
      if (newValue === null) {
        console.log(name + " removed");
      }
    }
  • 16. Provide default attribute values.
    Getters should return safe defaults when attributes are missing.
    get type() {
      return this.getAttribute("type") || "info";
    }
    
    get message() {
      return this.getAttribute("message") || "";
    }
  • 17. Use attributes for styling hooks.
    Reflected attributes are useful with :host() selectors inside Shadow DOM.
    :host([type="success"]) {
      display: block;
    }
    
    :host([disabled]) {
      opacity: 0.6;
    }
  • 18. Use attributes for accessible state.
    Attributes can help expose state to assistive technologies using ARIA.
    <my-accordion
      aria-expanded="false"
      aria-controls="panel-1">
    </my-accordion>
  • 19. Keep ARIA attributes synchronized.
    When component state changes, update related ARIA attributes too.
    set open(value) {
      if (value) {
        this.setAttribute("open", "");
      } else {
        this.removeAttribute("open");
      }
    
      this.setAttribute(
        "aria-expanded",
        String(Boolean(value))
      );
    }
  • 20. Avoid attributes for complex data.
    Arrays, objects, functions, dates, maps, sets, and class instances should usually be passed as properties.
    const table =
      document.querySelector("my-table");
    
    table.rows =
      [
        { id: 1, name: "Alice" },
        { id: 2, name: "Bob" }
      ];
  • 21. Avoid large JSON attributes.
    JSON attributes are fragile and harder to maintain. Prefer JavaScript properties for structured data.
    <my-table data='[{"id":1}]'>
    </my-table>
  • 22. Parse JSON attributes safely when needed.
    If a component supports JSON attributes, handle parsing errors.
    get config() {
      try {
        return JSON.parse(
          this.getAttribute("config") || "{}"
        );
      } catch (error) {
        return {};
      }
    }
  • 23. Use data-* attributes for custom metadata.
    data-* attributes store simple custom metadata and are available through dataset.
    <my-card
      data-track-id="billing-card">
    </my-card>
    const trackId =
      this.dataset.trackId;
  • 24. Reflect useful public state.
    Reflecting state to attributes helps styling, debugging, testing, and documentation.
    set selected(value) {
      if (value) {
        this.setAttribute("selected", "");
      } else {
        this.removeAttribute("selected");
      }
    }
  • 25. Avoid reflecting high-frequency state.
    Do not reflect values that change rapidly, such as mouse position, scroll position, animation frame data, or drag coordinates.
    this._dragX =
      event.clientX;
    
    this._dragY =
      event.clientY;
  • 26. Use attributes for server-rendered components.
    Attributes make Web Components easier to render from HTML templates, CMS pages, static sites, and server-rendered apps.
    <my-product-card
      product-id="101"
      title="Laptop | PHPKINGDOM"
      price="999">
    </my-product-card>
  • 27. Initialize from existing attributes.
    A component should read attributes that already exist in markup when it connects.
    connectedCallback() {
      if (!this.shadowRoot) {
        this.attachShadow(
          { mode: "open" }
        );
    
        this.render();
      }
    
      this.updateFromAttributes();
    }
  • 28. Test runtime attribute updates.
    Components should update when attributes change after initial render.
    const alert =
      document.querySelector("my-alert");
    
    alert.setAttribute(
      "type",
      "warning"
    );
    
    alert.setAttribute(
      "message",
      "Check required fields."
    );
  • 29. Document supported attributes.
    Public components should document attribute name, type, allowed values, default value, whether it reflects, and related property name.
  • 30. Keep attributes predictable.
    A good attribute API is simple, consistent, typed through conversion, validated, accessible, and easy to use from plain HTML.

Complete Web Component Attributes Example

This example shows string, boolean, numeric, and ARIA-related attributes with safe defaults and update logic.

class MyAlert extends HTMLElement {
  static get observedAttributes() {
    return [
      "type",
      "message",
      "dismissible",
      "open"
    ];
  }

  constructor() {
    super();

    this.attachShadow(
      { mode: "open" }
    );

    this.shadowRoot.innerHTML =
      `
        <style>
          :host {
            display: block;
          }

          :host(:not([open])) {
            display: none;
          }

          .alert {
            border: 1px solid #dbe5f1;
            border-radius: 0.75rem;
            padding: 1rem;
          }

          :host([type="success"]) .alert {
            border-color: #bbf7d0;
          }

          :host([type="danger"]) .alert {
            border-color: #fecaca;
          }
        </style>

        <div class="alert" role="status">
          <span class="message"></span>
          <button type="button" hidden>
            Dismiss
          </button>
        </div>
      `;
  }

  connectedCallback() {
    if (!this.hasAttribute("open")) {
      this.setAttribute("open", "");
    }

    this.update();

    this.shadowRoot
      .querySelector("button")
      .addEventListener("click", () => {
        this.removeAttribute("open");

        this.dispatchEvent(
          new CustomEvent(
            "alert-dismiss",
            {
              bubbles: true,
              composed: true
            }
          )
        );
      });
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (oldValue === newValue) {
      return;
    }

    this.update();
  }

  get type() {
    const allowed =
      ["info", "success", "warning", "danger"];

    const value =
      this.getAttribute("type");

    return allowed.includes(value)
      ? value
      : "info";
  }

  set type(value) {
    this.setAttribute(
      "type",
      value
    );
  }

  get message() {
    return this.getAttribute("message") || "";
  }

  set message(value) {
    this.setAttribute(
      "message",
      String(value)
    );
  }

  get dismissible() {
    return this.hasAttribute("dismissible");
  }

  set dismissible(value) {
    if (value) {
      this.setAttribute("dismissible", "");
    } else {
      this.removeAttribute("dismissible");
    }
  }

  get open() {
    return this.hasAttribute("open");
  }

  set open(value) {
    if (value) {
      this.setAttribute("open", "");
    } else {
      this.removeAttribute("open");
    }
  }

  update() {
    if (!this.shadowRoot) {
      return;
    }

    const message =
      this.shadowRoot.querySelector(".message");

    const button =
      this.shadowRoot.querySelector("button");

    message.textContent =
      this.message;

    button.hidden =
      !this.dismissible;

    this.setAttribute(
      "aria-hidden",
      String(!this.open)
    );
  }
}

customElements.define(
  "my-alert",
  MyAlert
);
<my-alert
  type="success"
  message="Profile saved successfully."
  dismissible
  open>
</my-alert>
const alert =
  document.querySelector("my-alert");

alert.setAttribute(
  "type",
  "warning"
);

alert.message =
  "Please review your changes.";

alert.dismissible =
  true;

Common Web Component Attributes Table

Attribute Type Example Common Use
label String label="Save" Button text or field label.
variant String variant="primary" Visual style.
size String size="large" Component size.
disabled Boolean disabled Disable interaction.
open Boolean open Expanded or visible state.
selected Boolean selected Selected state.
count Number string count="5" Numeric configuration.
aria-label String aria-label="Close" Accessible name.
data-* String data-track-id="card" Custom metadata.

Common Web Components Attribute Mistakes

  • Forgetting that attributes are always strings.
  • Using disabled="false" and expecting it to mean false.
  • Not listing important attributes in observedAttributes.
  • Using attributes for large objects or arrays.
  • Not validating attribute values.
  • Not providing default values for missing attributes.
  • Not syncing attributes with internal Shadow DOM controls.
  • Forgetting to update ARIA attributes when state changes.
  • Reflecting high-frequency internal state into attributes.
  • Not documenting public attributes for component consumers.

Web Components Attribute Best Practices

  • Use attributes for simple public configuration.
  • Use properties for complex JavaScript data.
  • Use boolean attributes by presence.
  • Use kebab-case for multi-word attributes.
  • Convert numeric strings safely.
  • Validate allowed values such as variants and sizes.
  • Observe only attributes that affect rendering or behavior.
  • Keep accessibility attributes synchronized.
  • Test initial attributes and runtime changes.
  • Document attribute names, types, defaults, and allowed values.

Pro Tip

Use attributes for values that make sense in HTML, such as variant, size, disabled, open, and label. Use properties for real JavaScript data such as arrays, objects, callbacks, and service instances.