· Experiences · 2 min read
Reactivity in Astro
As I was learning to use the Astro framework, I wanted to learn about idiomatic implementations within Astro. Here is what I learned about implementing reactivity in Astro.

While using Astro, I liked how the HTML and CSS can be dynamically generated, however, I hit an issue when building reactive components:
---
function onclick() {
console.log('clicked')
}
---
<button onclick="{onclick}">Test</button>
Having lived in both svelte and react, you may have expected the above Astro code to be correct and functional. However, the Frontmatter ---
fence is purely there for the separation of server-side and client-side code only. Astro does not expose server-side functions to the client, even if they are referenced.
There are a few ways to solve this. On my journey, I had reviewed that Astro has great integrations with a client-side framework, knowing that they did not have strong reactivity support, so I tried adding Vue to my stack.
<script lang="ts" setup>
function onclick() {
console.log('click');
}
</script>
<template>
<button @click="onclick">Test</button>
</template>
Although this added reactivity, the syntax difference made it awkward to work with both Vue and Astro. Also, adding the client-side framework also increased my build times on every change since we also had to trigger the framework transpilation. But the biggest problem that I ran into, was an issue where Vue components cannot import Astro components, even though Astro components can import Vue components. In my exploration, I learned that this was also true when trying to integrate Svelte.
The solution was to RTFM and actually learn to work within Astro. I read common script patterns and learned that you need to add a client-side script and work within the browser’s JavaScript. I wasn’t ready to jump back into the raw wild west since I was already invested in the Astro framework. Reading on, I found my answer with Astro web components. This is using the web components api to create custom html elements to bind reactivity. You can still bind Astro server-side properties to these web components.
---
const { message = 'clicked' } = Astro.props;
---
<astro-button>
<button>Test</button>
</astro-button>
<script define:vars="{{" message }}>
class AstroButton extends HTMLElement {
// Handle all reactivity in the connected callback
connectedCallback() {
const button = this.querySelector('button');
button.addEventListener('click', () => {
console.log(message);
});
}
}
customElements.define('astro-button', AstroButton);
</script>
Final Thoughts
It took some time to understand the point of view of Astro on reactivity. Although Astro web components do have a little more boilerplate than Vue and Svelte, it is compact and simple enough to handle my logic and reactivity.