The Complete Guide to Building with Qwik in 2026: The Resumable Framework
Qwik emerged as the framework that eliminates hydration overhead entirely through "resumability" — the idea that instead of re-executing JavaScript on the client, the server execution state is serialized and resumed. For content sites, this means instant interactivity with zero JavaScript shipped initially.
Here's the practical guide.
Core Concept: Resumability
React: Server renders HTML, then client downloads JS, then re-runs ALL components (hydration)
Qwik: Server renders HTML, serializes execution state, client resumes — no re-execution
The result: A Qwik app can be interactive before any JS downloads.
Basic Component
import { component$ } from "@builder.io/qwik";
export const Counter = component$(() => {
const count = useSignal(0);
return (
<div>
<p>Count: {count.value}</p>
<button onClick$={() => count.value++}>
Increment
</button>
</div>
);
});
Note onClick$ — the $ means this handler is lazy-loaded only when clicked.
useSignal
import { component$, useSignal } from "@builder.io/qwik";
export const Profile = component$(() => {
const name = useSignal("Alice");
const age = useSignal(30);
return (
<div>
<input
value={name.value}
onInput$={(e) => {
name.value = (e.target as HTMLInputElement).value;
}}
/>
<p>{name.value} is {age.value} years old</p>
</div>
);
});
useStore
import { component$, useStore } from "@builder.io/qwik";
export const TodoList = component$(() => {
const todos = useStore([
{ id: 1, text: "Learn Qwik", done: true },
{ id: 2, text: "Build something", done: false },
]);
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.done}
onClick$={() => todo.done = !todo.done}
/>
{todo.text}
</li>
))}
</ul>
);
});
Server Functions (Server$)
import { component$, useSignal, server$ } from "@builder.io/qwik";
const searchAPI = server$(async (query: string) => {
// This runs on the server!
const results = await db.search(query);
return results;
});
export const Search = component$(() => {
const query = useSignal("");
const results = useSignal<string[]>([]);
return (
<div>
<input
value={query.value}
onInput$={async (e) => {
query.value = (e.target as HTMLInputElement).value;
if (query.value.length > 2) {
results.value = await searchAPI(query.value);
}
}}
/>
<ul>
{results.value.map((r) => (
<li key={r}>{r}</li>
))}
</ul>
</div>
);
});
Route Loaders
// src/routes/index.tsx
import { routeLoader$ } from "@builder.io/qwik-city";
export const useServerTime = routeLoader$(() => {
return { time: new Date().toISOString() };
});
export default component$(() => {
const serverTime = useServerTime();
return (
<div>
Server time: {serverTime.value.time}
</div>
);
});
When to Use Qwik
Good for:
- Content sites (documentation, blogs, marketing)
- E-commerce product pages
- Any site where initial load performance matters
Still use React for:
- Complex client-side state
- Heavy interactivity
- Native app-like experiences
This article contains affiliate links. If you sign up through the links above, I may earn a commission at no additional cost to you.
Ready to Build Your Online Business?
Get started with Systeme.io for free — All-in-one platform for building your online business with AI tools.
Top comments (0)