Attribute Signals By default, each element is observed with a MutationObserver watching all attributes. Changes to any attribute trigger the attributeChangedCallback and you can access all attributes as signals. This makes it much less cumbersome to write reactive attributes. const MyElement = customElement(({ attrSignals }) => { const [heading, setHeading] = attrSignals['my-heading']; // setHeading() will also update the `my-heading` attribute in the HTML. return html`<h2>${heading}</h2>`; }); However, the MutationObserver does impose a small performance tradeoff that may add up if you render a lot of elements. To better optimize for performance, you can pass observedAttributes to the options. Doing so will disable the MutationObserver, and only the observed attributes will trigger the attributeChangedCallback. const MyElement = customElement(({ attrSignals }) => { const [heading, setHeading] = attrSignals['my-heading']; return html`<h2>${heading}</h2>`; }, { observedAttributes: ['my-heading'] }); Usage: <my-element my-heading="My Element's Title"></my-element> NOTICE: Since attrSignals is a Proxy object, any property will return a signal and auto-bind it to the attribute it corresponds with. For information on syncing attributes with properties, see the Property Signals documentation.