Problem
<html lang="en">
  <body>
    <user-profile id="profile" username="JohnDoe"></user-profile>

    <script>
      class UserProfile extends HTMLElement {
        constructor() {
          super();
          this.attachShadow({ mode: "open" });
        }

        connectedCallback() {
          this.render();
        }

        render() {
          this.shadowRoot.innerHTML = `
            <p>Username: ${this.getAttribute("username")}</p>
          `;
        }
      }

      customElements.define("user-profile", UserProfile);

      setTimeout(() => {
        document.getElementById("profile").setAttribute("username", "JaneDoe");
      }, 3000);
    </script>
  </body>
</html>
Solution

The observedAttributes static getter and attributeChangedCallback method are missing. Without these, the component will not react to changes in its attributes after the initial render.

<html lang="en">
  <body>
    <user-profile id="profile" username="JohnDoe"></user-profile>

    <script>
      class UserProfile extends HTMLElement {
        constructor() {
          super();
          this.attachShadow({ mode: "open" });
        }

        connectedCallback() {
          this.render();
        }

        static get observedAttributes() {
          return ["username"];
        }

        attributeChangedCallback(name, oldValue, newValue) {
          if (name === "username") {
            this.render();
          }
        }

        render() {
          this.shadowRoot.innerHTML = `
            <p>Username: ${this.getAttribute("username")}</p>
          `;
        }
      }

      customElements.define("user-profile", UserProfile);

      setTimeout(() => {
        document.getElementById("profile").setAttribute("username", "JaneDoe");
      }, 3000);
    </script>
  </body>
</html>