Qwik
Using Qwik components as resumable islands in Avalon.
Setup
Add 'qwik' to the integrations array:
const plugins = await avalon({
integrations: ['qwik'],
});
Install 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.
File name
Qwik islands use the .qwik.tsx extension. Avalon compiles those files with @builder.io/qwik as the JSX runtime. A /** @jsxImportSource @builder.io/qwik */ comment is optional and overrides the filename.
Writing a Qwik island
Use component$ to define them:
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 '../components/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:
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:
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.