Before doing this, I did some experiments with dynamically rendering astro pages. After achieving that, I tested it out with this page, rendering a React, Alpine, and Svelte component in one page.
Below are different implementations of the same counter logic and some shared state.
Astro land
The initial framework is Astro.
React.js
import { useState } from 'react'
import { useStore } from '@nanostores/react'
import { framework } from './stores/store'
function Counter() {
const [count, setCount] = useState(0)
return (
<button
type="button"
onClick={() => setCount(count + 1)}
className="bg-indigo-500 text-white px-2 py-1 rounded-md"
>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
)
}
function SharedState() {
const $framework = useStore(framework)
return (
<button
type="button"
onClick={() => framework.set('React')}
className="bg-indigo-500 text-white px-2 py-1 rounded-md"
>
The current framework is{' '}
<span className="text-white font-bold">{$framework}</span>.
</button>
)
}
export default function ReactComponent() {
return (
<div className="flex flex-col gap-4">
<Counter />
<SharedState />
</div>
)
}
Alpine.js
<div
x-data="{ count: 0, framework: utils().store.framework.get(), store: utils().store.framework }"
class="flex flex-col gap-4"
>
<button
type="button"
@click="count++"
class="bg-indigo-500 text-white px-2 py-1 rounded-md"
x-text="`Clicked ${count} ${count === 1 ? 'time' : 'times'}`"
>
Clicked 0 times
</button>
<!-- I dunno why but no console.log in `x-init` makes the store.subscribe not work -->
<button
type="button"
x-init="console.log('Loading Alpine'); store.subscribe((value) => framework = value)"
@click="store.set('Alpine')"
class="bg-indigo-500 text-white px-2 py-1 rounded-md"
x-html="`The current framework is <b>${framework}</b>`"
>
The current framework is ...
</button>
</div>
Svelte
<script>
import { framework } from './stores/store'
let count = $state(0)
</script>
<div class="flex flex-col gap-4">
<button
onclick={() => count += 1}
class="bg-indigo-500 text-white px-2 py-1 rounded-md"
>
Clicked {count}
{count === 1 ? 'time' : 'times'}
</button>
<button
type="button"
onclick={() => framework.set('Svelte')}
class="bg-indigo-500 text-white px-2 py-1 rounded-md"
>
The current framework is{' '}
<span class="text-white font-bold">{$framework}</span>.
</button>
</div>
Vue
<script setup>
import { ref } from 'vue'
import { useStore } from '@nanostores/vue'
import { framework as store } from './stores/store'
const count = ref(0)
const framework = useStore(store)
</script>
<template>
<div class="flex flex-col gap-4">
<button
type="button"
@click="count++"
class="bg-indigo-500 text-white px-2 py-1 rounded-md"
>
Clicked {{ count }} {{ count === 1 ? 'time' : 'times' }}
</button>
<button
type="button"
@click="store.set('Vue')"
class="bg-indigo-500 text-white px-2 py-1 rounded-md"
>
The current framework is
<span class="text-white font-bold">{{ framework }}</span
>.
</button>
</div>
</template>