Announcing Reciprocate, a Sweet Solution for Making Your HTML Web Components Reactive

Have you ever been chipping away on vanilla #WebComponents when you began to wonder “hey waitaminute…how do I make it so when I set this #JavaScript property, the equivalent #HTML attribute is updated? And if I set this attribute, the equivalent property is updated? Or when either is updated, my component will re-render?! Where are the APIs for this stuff??”

Worry no longer! 🤓 #WebDev

https://thathtml.blog/2025/09/reciprocate-reactivity-for-html-web-components/

Announcing Reciprocate, a Sweet Solution for Making Your HTML Web Components Reactive

A new helper utility for adding signal-based reactivity and attribute/property reflection to any vanilla custom element class.

That HTML Blog

@vanillaweb Cool! I especially like how you can pass in different signal libs.

Definitely an interesting design space. Thus I have to ask: anything in particular you wanted that https://mastrojs.github.io/reactive/ couldn’t do or couldn’t do well? I still have to look at Reciprocate’s details, but at first sight seems more boiler plate? Is that giving you more control somehow? I see you’re watching attributes, which Reactive Mastro doesn’t do by default. Was this such a common thing that external code sets those and your component needs to react?

Reactive Mastro – GUI library for MPAs

A tiny reactive GUI library for your existing MPA.

@mb21 @vanillaweb Thanks! So the main goal here is to make it easy to build a public component API. In the counter example, the component element has a `count` property, and that could be updated from anywhere in JS (`el.count = 123`). Similarly, the attribute could be set instead (`el.setAttribute("count", "123")`). And of course reading a property or attribute always provides the latest value. Finally, you can write an effect to do something to the DOM in reaction to those value updates.
@mb21 @vanillaweb Reactive Mastro is a very cool project as well! Big difference there is you offer a full base class for component authors to inherit from. Reciprocate is simply a utility, authors still inherit directly from `HTMLElement` or write their own base class. And something at a framework level, like Mastro, could even utilize Reciprocate within its own base class.

@jaredwhite @mb21 @vanillaweb I see. The first thing works with Reactive Mastro as well, e.g. document.querySelector('my-counter').count.set(7) on https://mastrojs.github.io/reactive/
But yes, I've always viewed attributes as best left unchanged after the server rendered them. If you have JS updating something, best to encourage properties, which are not just strings? Perhaps I'm missing a use-case? But feels like one of the more annoying aspects of the DOM (and by extension web components).

And yes, it's true that you have to inherit from the ReactiveElement class. I just didn't find a way to reduce the boiler plate without that. At least you can call customElements.define yourself, so strictly speaking it's still a library and not a framework 😉

Reactive Mastro – GUI library for MPAs

A tiny reactive GUI library for your existing MPA.

@mb21 @vanillaweb One benefit to attribute reflection is you can style based on that. Things like "variant" or "size", or a boolean where the attribute is either present or missing. If I write code like `myBtn.spinner = true`, then the appearance of a `spinner` attribute could allow styling to be applied for that case.
@jaredwhite @mb21 @vanillaweb That’s a good point. A lot of built in properties of course reflect to the attribute. And for classes, we use classList anyway, in order to not lose other classes. But yes, a custom field like variant is not auto-reflected. For that, you’d have to add data-bind=“dataset.variant=variant” or similar. Do you think it’s worth adding the same feature for non-data attributes so you could do data-bind=“attributes.variant=variant” ?