Best Qwik and QwikCity Boilerplates 2026
Best Qwik and QwikCity Boilerplates in 2026
TL;DR
Qwik is the most architecturally distinctive JavaScript framework in 2026 — its resumability model completely eliminates hydration, the biggest performance bottleneck of server-side rendered React/Vue/Svelte apps. Instead of shipping the entire component tree's JS to the client and re-running it to "hydrate," Qwik serializes component state into the HTML and downloads individual component code lazily, only when needed. The result: Time to Interactive (TTI) is near-zero, regardless of app complexity. For content-heavy apps, marketing sites, and e-commerce where Core Web Vitals directly affect search ranking, Qwik is the most compelling framework in 2026. For interactive SaaS apps with complex state, SolidJS or React remains the better choice.
Key Takeaways
- Resumability vs. Hydration: React/Vue/Svelte hydrate by re-running all JS on client load; Qwik resumes exactly where the server left off, downloading code only for interactions the user actually triggers
- QwikCity is Qwik's meta-framework (equivalent to Next.js for React or SvelteKit for Svelte) — routing, loaders, actions, middleware all included
- Lighthouse scores for Qwik apps are consistently near-perfect — TTI < 1s even for complex pages with heavy content
- Builder.io built Qwik and uses it in production for their visual CMS platform — enterprise backing with real production validation
- ~500K weekly downloads — smaller than Next.js but growing; ecosystem is thinner but production-ready
- Best use cases: content sites, marketing pages, e-commerce, documentation, any app where SEO and Core Web Vitals directly affect revenue
The Resumability Model: Why It's Different
The Hydration Problem
Every framework except Qwik uses hydration. When a React (or Vue, Svelte, Solid) page is server-rendered:
- Server runs component code and produces HTML
- HTML ships to the browser (fast first paint)
- Browser downloads the JavaScript bundle
- Framework re-runs all component code to attach event listeners ("hydration")
- App is now interactive
Step 4 is pure overhead — the server already ran this code. For a complex page with hundreds of components, hydration can take 2-5 seconds on a mobile device. During this window, the page looks interactive but isn't.
Qwik's Resumability
Qwik takes a different approach:
- Server runs component code and produces HTML with serialized state embedded in it
- HTML ships to the browser (fast first paint)
- Browser downloads only ~1kB of Qwik framework code — just enough to handle the next user interaction
- When a user clicks a button, Qwik downloads only the code for that button's click handler
- App becomes interactive for that specific interaction in milliseconds
The key insight: you don't need the entire JavaScript application upfront. You only need the code for the interaction the user is about to perform.
// A Qwik component — looks like React but works fundamentally differently
import { component$, useSignal, $ } from '@builder.io/qwik'
export const Counter = component$(() => {
const count = useSignal(0)
// The click handler is defined with $() — this marks it as lazy-loadable
// Qwik will only download this function when the user clicks
const increment = $(() => {
count.value++
})
return (
<div>
<p>Count: {count.value}</p>
<button onClick$={increment}>+1</button>
</div>
)
})
The $() suffix throughout Qwik code marks "boundaries" — points where Qwik can split the code into lazy-loadable chunks. This is the fundamental mechanism of resumability.
QwikCity: The Meta-Framework
QwikCity is Qwik's full-featured meta-framework:
File-Based Routing
src/routes/
├── index.tsx → /
├── about/
│ └── index.tsx → /about
├── products/
│ ├── index.tsx → /products
│ └── [id]/
│ └── index.tsx → /products/:id
└── api/
└── products.ts → /api/products (JSON endpoint)
Server Loaders and Actions
// src/routes/products/[id]/index.tsx
import { component$ } from '@builder.io/qwik'
import { routeLoader$, routeAction$, Form, zod$, z } from '@builder.io/qwik-city'
// Server-side data loader — runs before the component renders
export const useProduct = routeLoader$(async ({ params, error }) => {
const product = await db.products.findById(params.id)
if (!product) throw error(404, 'Product not found')
return product
})
// Server-side action — for form submissions
export const useAddToCart = routeAction$(
async (data, { cookie, error }) => {
const session = await getSession(cookie)
if (!session) throw error(401, 'Not authenticated')
await db.carts.addItem(session.userId, data.productId, data.quantity)
return { success: true }
},
zod$({ productId: z.string(), quantity: z.number().min(1) })
)
export default component$(() => {
const product = useProduct()
const addToCart = useAddToCart()
return (
<div>
<h1>{product.value.name}</h1>
<p>${product.value.price}</p>
<Form action={addToCart}>
<input type="hidden" name="productId" value={product.value.id} />
<input type="number" name="quantity" defaultValue="1" />
<button type="submit">Add to Cart</button>
</Form>
</div>
)
})
This pattern is similar to Remix's loader/action model — server data runs on the server, the component renders the result.
The Best QwikCity Starters
1. Official QwikCity App Starter
The official starting point from Builder.io:
npm create qwik@latest my-app
# Choose: Basic App (QwikCity)
Includes: QwikCity routing, Tailwind CSS, TypeScript, Vite
This is the base for most community starters. It's minimal by design — add what you need.
2. Qwik + Drizzle + Turso Starter
For SaaS-grade database integration:
npx create qwik@latest --example drizzle-turso
This starter integrates:
- QwikCity for routing and server actions
- Drizzle ORM with Turso (SQLite edge database)
- Cloudflare Workers adapter
- TypeScript throughout
Turso + Cloudflare Workers is a compelling edge-native stack — your app runs in 300+ edge locations with an SQLite database replicated globally.
3. Qwik + Auth.js Starter
npx create qwik@latest --example auth-qwik
Auth.js (formerly NextAuth) has a QwikCity adapter — the familiar credentials + OAuth flow with GitHub/Google/Discord providers.
4. QwikStart (Community SaaS Starter)
The community-maintained SaaS starter with more batteries included:
GitHub: qwikstart/qwikstart
Includes:
- QwikCity + TypeScript
- Prisma + PostgreSQL
- Better Auth
- Stripe subscriptions
- Tailwind CSS + shadcn-qwik components
- Resend for email
This is the QwikCity equivalent of ShipFast — a paid-first product but community alternatives exist.
Qwik Performance Benchmarks
Real-world data from Builder.io and community benchmarks:
| Metric | Qwik | Next.js (SSR) | React SPA |
|---|---|---|---|
| Initial JS sent | ~1kB | 150-400kB | 200-600kB |
| Time to Interactive (TTI) | < 200ms | 1-4s | 2-6s |
| Lighthouse Performance | 95-100 | 60-85 | 50-75 |
| Core Web Vitals pass rate | ~95% | ~65% | ~40% |
These are general benchmarks — actual numbers vary widely based on app complexity, image optimization, and CDN configuration. But the trend is consistent: Qwik gets better Lighthouse scores with less effort than React.
When Qwik Beats React (And When It Doesn't)
Qwik Wins For:
- E-commerce product pages — each additional 100ms of TTI costs 1-2% conversion; Qwik's near-zero TTI is directly measurable revenue impact
- Marketing and landing pages — Google uses Core Web Vitals as a ranking signal; Qwik's consistent 95+ Lighthouse scores give an SEO edge
- Documentation sites — lots of content, minimal interactivity; Qwik's lazy-loading means readers pay zero cost for features they don't use
- Content-heavy dashboards — reporting dashboards with large data tables where initial load performance matters
React/Next.js Wins For:
- Complex interactive SaaS apps — when 80% of interactions happen after the page loads, hydration cost is a one-time investment; Qwik's lazy-loading model adds overhead for highly interactive UIs
- Rich component ecosystems — shadcn/ui, Radix, React Query, Zustand — the React ecosystem is incomparably larger
- Team familiarity — the learning curve for Qwik's
$()boundary model is real; React skills are more transferable - Complex forms and state machines — React's
useReducer, Zustand, and TanStack Form are more mature
QwikCity Middleware and Auth Patterns
QwikCity's middleware system handles auth, rate limiting, and request processing at the edge:
// src/middleware.ts
import type { RequestHandler } from '@builder.io/qwik-city'
export const onRequest: RequestHandler = async ({ url, cookie, redirect }) => {
const protectedPaths = ['/dashboard', '/settings', '/api/admin']
const isProtected = protectedPaths.some(p => url.pathname.startsWith(p))
if (isProtected) {
const sessionToken = cookie.get('session_token')?.value
if (!sessionToken) {
throw redirect(302, `/login?redirect=${url.pathname}`)
}
// Verify session — this runs at the edge before the route handler
const session = await verifySession(sessionToken)
if (!session) {
cookie.delete('session_token')
throw redirect(302, '/login')
}
}
}
API Routes for JSON Endpoints
QwikCity API routes work like Next.js API routes but run at the edge:
// src/routes/api/products/index.ts
import type { RequestHandler } from '@builder.io/qwik-city'
import { db } from '~/server/db'
export const onGET: RequestHandler = async ({ json, query }) => {
const limit = parseInt(query.get('limit') ?? '20')
const cursor = query.get('cursor') ?? undefined
const products = await db.products.findMany({
take: limit,
cursor: cursor ? { id: cursor } : undefined,
orderBy: { createdAt: 'desc' },
})
json(200, { products, nextCursor: products.at(-1)?.id })
}
export const onPOST: RequestHandler = async ({ request, json, cookie, error }) => {
const session = await getSession(cookie)
if (!session?.isAdmin) throw error(403, 'Admin required')
const body = await request.json()
const product = await db.products.create({ data: body })
json(201, product)
}
Deploying QwikCity Apps
QwikCity ships adapters for all major deployment targets:
npm run qwik add cloudflare-pages # Cloudflare Pages
npm run qwik add vercel-edge # Vercel Edge Functions
npm run qwik add netlify-edge # Netlify Edge Functions
npm run qwik add nodejs # Node.js (Railway, Fly.io)
npm run qwik add aws-lambda # AWS Lambda
npm run qwik add azure-swa # Azure Static Web Apps
Cloudflare Pages is the recommended production target for Qwik — the combination of Qwik's lazy-loading with Cloudflare's global edge network gives the best real-world performance. Your app runs in 300+ edge locations; assets are cached at the network level; the small Qwik runtime minimizes cold start overhead.
# Build for Cloudflare Pages
npm run build
# Preview locally with Wrangler
npx wrangler pages dev ./dist
# Deploy
npx wrangler pages deploy ./dist --project-name my-qwik-app
The $() Boundary Model: The Learning Curve
The biggest adjustment for React developers is Qwik's $() boundary system. Any function that crosses the server/client boundary (or that needs to be lazy-loaded) must be wrapped in $():
// ❌ Won't work — inline function can't be serialized
<button onClick={() => console.log('clicked')}>Click me</button>
// ✅ Works — $ marks the boundary, Qwik lazy-loads this function
<button onClick$={() => console.log('clicked')}>Click me</button>
// ✅ Also works — function defined with $() outside JSX
const handleClick = $(() => console.log('clicked'))
<button onClick$={handleClick}>Click me</button>
This constraint catches React developers off-guard initially. Once internalized, it becomes natural — and it's what enables Qwik's lazy-loading magic.
Methodology
- Benchmark data from Builder.io (builder.io/blog), Web Almanac (HTTP Archive), and community benchmarks
- npm download data from npmjs.com API, March 2026 weekly averages
- Framework version: Qwik v2.x, QwikCity v2.x
- Sources: Qwik official documentation (qwik.dev), Builder.io blog
Find Qwik starters and compare performance metrics on StarterPick — see how Qwik boilerplates compare to Next.js alternatives.
Related: Best SolidJS Boilerplates 2026 · Best Next.js Boilerplates 2026 · Edge-First SaaS Boilerplates: Cloudflare Workers 2026