Qwik
Using Qwik components as resumable islands in Avalon.
Setup
Add 'qwik' to the integrations array:
const plugins = await avalon({
integrations: ['qwik'],
});
Install Qwik:
bun add @builder.io/qwik
How Qwik differs
Qwik doesn't hydrate — it resumes. The server serializes component state directly into the HTML, and the Qwikloader (~1KB inline script) lazily loads handlers only when events fire. There's no re-execution of component code on the client.
Because of this, Qwik islands don't use the island prop. They're always resumable by default.
JSX pragma
Qwik islands require the /** @jsxImportSource @builder.io/qwik */ pragma:
/** @jsxImportSource @builder.io/qwik */
import { component$ } from '@builder.io/qwik';
Writing a Qwik island
Qwik islands use the .qwik.tsx extension. Use component$ to define them:
/** @jsxImportSource @builder.io/qwik */
import { component$, useSignal } from '@builder.io/qwik';
import { defineQwikIsland } from '@useavalon/qwik/island';
const Counter = component$(() => {
const count = useSignal(0);
return (
<button onClick$={() => count.value++}>
Count: {count.value}
</button>
);
});
export default defineQwikIsland(Counter);
The defineQwikIsland wrapper is a zero-cost identity function — it returns the component unchanged at runtime but provides Preact-compatible types so TypeScript accepts <Counter /> in page files. See Type Compatibility for why this is needed.
Using the island
import Counter from '../islands/Counter.qwik.tsx';
export default function Page() {
return (
<div>
<Counter />
</div>
);
}
No island prop needed — Qwik handles resumability on its own.
Reactive primitives
Qwik uses signals for fine-grained reactivity:
/** @jsxImportSource @builder.io/qwik */
import { component$, useSignal, useComputed$ } from '@builder.io/qwik';
import { defineQwikIsland } from '@useavalon/qwik/island';
const DoubleCounter = component$(() => {
const count = useSignal(0);
const doubled = useComputed$(() => count.value * 2);
return (
<div>
<button onClick$={() => count.value++}>Increment</button>
<p>Doubled: {doubled.value}</p>
</div>
);
});
export default defineQwikIsland(DoubleCounter);
Visible tasks
Use useVisibleTask$ for code that should run when the component becomes visible in the viewport:
/** @jsxImportSource @builder.io/qwik */
import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik';
import { defineQwikIsland } from '@useavalon/qwik/island';
const Timer = component$(() => {
const elapsed = useSignal(0);
useVisibleTask$(() => {
const id = setInterval(() => elapsed.value++, 1000);
return () => clearInterval(id);
});
return <p>Elapsed: {elapsed.value}s</p>;
});
export default defineQwikIsland(Timer);
Caveats
- No
islandprop — Qwik manages its own resumability. Addingisland={{ condition: '...' }}has no effect. $boundaries matter — Code after a$(likeonClick$,useVisibleTask$) is lazy-loaded. Keep expensive logic behind$boundaries for optimal performance.- Signals are objects — Access values with
.value. Writingcountinstead ofcount.valuegives you the signal object, not the number.