AvalonAvalon
GitHub

Vue

Using Vue 3 components as islands in Avalon.

Setup

Add 'vue' to the integrations array:

const plugins = await avalon({
  integrations: ['vue'],
});

Install Vue:

>_ Terminal
bun add vue

Writing a Vue island

Vue islands are standard .vue single-file components. Use the Composition API with <script setup> just like you normally would:

<!-- src/components/Counter.vue -->
<script setup lang="ts">
import { ref } from 'vue';

const props = defineProps<{ initialCount?: number }>();
const count = ref(props.initialCount ?? 0);
</script>

<template>
  <button @click="count++">
    Count: {{ count }}
  </button>
</template>

Using the island

import Counter from '../components/Counter.vue';

export default function Page() {
  return (
    <div>
      <Counter initialCount={5} island={{ condition: 'on:visible' }} />
    </div>
  );
}

Composables

Vue composables work inside islands as expected:

<!-- src/components/PersistedCounter.vue -->
<script setup lang="ts">
import { useLocalStorage } from '@vueuse/core';

const count = useLocalStorage('count', 0);
</script>

<template>
  <button @click="count++">
    Count: {{ count }}
  </button>
</template>

Scoped styles

Vue scoped styles work out of the box. The integration extracts and applies scoped CSS during SSR:

import { ref } from 'vue';
const count = ref(0);
</script>

<template>
  <button class="counter" @click="count++">
    Count: {{ count }}
  </button>
</template>

<style scoped>
.counter {
  background: #42b883;
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  cursor: pointer;
}
</style>

Caveats

  • Stores are scoped to the island instance — Pinia stores or reactive objects created inside an island are not shared with other islands on the page. Each island is an independent Vue app. For live cross-island updates, use DOM CustomEvents; avoid shared module-level stores that can leak during SSR.
  • No SSR prop drilling via provide/inject — provide/inject only works within a single island's component tree. Pass initial data as props on the island component instead.