Qwik City Adapter
Resumability-first meta-framework with instant page loads, zero JS hydration overhead, 18 optimizer segments, and server actions. Verified: zero JS initial load on content pages.
Qwik 1.5 · Resumable · Zero JS
Resumability
Qwik serializes app state to HTML — on click, it resumes from HTML instead of re-executing JS.
Zero JS initial load
Content pages ship zero JavaScript by default. JS loads lazily only for the interactions you click.
18 optimizer segments
The Qwik optimizer splits components into fine-grained async segments automatically.
Server actions
routeAction$() handles form mutations server-side with automatic type-safe zod validation.
Signals
useSignal() and useStore() for reactive state with automatic subscriptions — no re-renders.
Edge-first SSR
Runs on Cloudflare Workers, Deno Deploy, or any Node.js runtime at the edge.
Setup
bash
npm create lunx-dev@latest my-qwik-app -- --framework qwik --ts cd my-qwik-app && npm install && npm run dev
Qwik component
typescriptsrc/components/counter/counter.tsx
import { component$, useSignal, useTask$ } from '@builder.io/qwik' // component$: lazily loaded — only runs when needed export const Counter = component$<{ initial?: number }>(({ initial = 0 }) => { const count = useSignal(initial) const doubled = useSignal(0) // useTask$: runs on server and client, auto-tracks dependencies useTask$(({ track }) => { track(() => count.value) // re-runs when count changes doubled.value = count.value * 2 }) return ( <div class="counter"> <h2>Count: {count.value}</h2> <p>Doubled: {doubled.value}</p> {/* onClick$: handler loaded lazily only when user clicks */} <button onClick$={() => count.value--}>−</button> <button onClick$={() => count.value++}>+</button> </div> ) })
Route loader and action
typescriptsrc/routes/blog/index.tsx
import { component$ } from '@builder.io/qwik' import { routeLoader$, routeAction$, Form, zod$, z } from '@builder.io/qwik-city' // Runs on server — data available before component renders export const usePosts = routeLoader$(async ({ env }) => { const apiUrl = env.get('API_URL') const posts = await fetch(`${apiUrl}/posts`).then(r => r.json()) return posts as Post[] }) // Server action with Zod validation export const useCreatePost = routeAction$( async (data, { fail }) => { const saved = await db.post.create({ data }) if (!saved) return fail(500, { message: 'Failed to create post' }) return { success: true, id: saved.id } }, zod$({ title: z.string().min(1).max(200), content: z.string().min(10), }) ) export default component$(() => { const posts = usePosts() const createPost = useCreatePost() return ( <div> <Form action={createPost}> <input name="title" placeholder="Title" /> <textarea name="content" placeholder="Content..." /> <button type="submit">Create Post</button> </Form> {posts.value.map(post => ( <article key={post.id}><h2>{post.title}</h2></article> ))} </div> ) })
Resumability vs Hydration
Traditional SSR frameworks (React, Vue) must re-execute all component JavaScript during hydration. Qwik serializes component state into HTML attributes — on interaction, it resumes from that serialized state without re-executing the entire component tree. This is why Qwik achieves nearly instant interactivity on any device, regardless of bundle size.