Canonical instruction file for AI coding agents working on the Qwik monorepo. For detailed contributor setup, see CONTRIBUTING.md.
Qwik is a resumable web framework — it serializes application state and framework state into HTML during SSR, then resumes on the client without re-executing component code. This enables it to stream javascript (a.k.a javascript streaming). There’s no waiting for the entire code to be downloaded (a.k.a hydration). The developer can write code very similar to other reactive frameworks; the app is automatically instant, regardless the amount of javascript.
Key concepts: Resumability, QRLs (lazy-loading primitives), the $ suffix transform, fine-grained signals, and the Rust-based optimizer.
| Package | Path | Description |
|---|---|---|
@builder.io/qwik |
packages/qwik |
Core framework (runtime + optimizer) |
@builder.io/qwik-city |
packages/qwik-city |
Meta-framework (routing, middleware, adapters) |
@builder.io/qwik-react |
packages/qwik-react |
React integration layer |
@builder.io/qwik-auth |
packages/qwik-auth |
Auth.js integration |
@builder.io/qwik-dom |
packages/qwik-dom |
Server-side DOM implementation |
@builder.io/qwik-worker |
packages/qwik-worker |
Web Worker support (experimental) |
@builder.io/qwik-labs |
packages/qwik-labs |
Experimental features (private) |
eslint-plugin-qwik |
packages/eslint-plugin-qwik |
ESLint rules for Qwik |
create-qwik |
packages/create-qwik |
Project scaffolding CLI |
qwik-docs |
packages/docs |
Documentation site (private) |
insights |
packages/insights |
Analytics dashboard (private) |
Requirements: Node ≥22.18.0, pnpm ≥10.14.0
pnpm install
pnpm build.local # builds everything without Rust (copies optimizer from npm)Use pnpm build.full only if you modified Rust/optimizer code.
Prefer pnpm build --qwik --qwikcity --dev to build qwik and qwik-city faster.
| Task | Command | Notes |
|---|---|---|
| Install | pnpm install |
|
| Install | pnpm install |
|
| Build (no Rust) | pnpm build.local |
For a fresh start |
| Build (with Rust) | pnpm build.full |
Only for optimizer changes |
| Build core only | pnpm build.core |
Fast — just Qwik + Qwik City + types |
| Watch mode | pnpm build.watch |
Rebuilds on change |
| Unit tests | pnpm test.unit |
Vitest — runs packages/**/*.unit.{ts,tsx} |
| E2E tests (Chromium) | pnpm test.e2e.chromium |
Playwright |
| E2E tests (City) | pnpm test.e2e.city |
Qwik City–specific E2E |
| Lint | pnpm lint |
ESLint + Prettier + Rust lint |
| Lint fix | pnpm lint.fix |
Auto-fix ESLint issues |
| Format | pnpm fmt |
Prettier + syncpack |
| Type check | pnpm tsc.check |
Full TypeScript check |
| Update API docs | pnpm api.update |
Regenerates public API .md files |
| Create changeset | pnpm change |
Interactive — creates .changeset/*.md |
| Dev server | pnpm serve |
Port 3300 |
| Docs dev | pnpm docs.dev |
Documentation site |
# Unit test — single file
pnpm vitest run packages/qwik/src/core/qrl/qrl.unit.ts
# E2E test — single file
pnpm playwright test starters/e2e/e2e.events.spec.ts --project chromiumQwik serializes the full application state into HTML at SSR time. On the client, it resumes from that serialized state instead of re-executing components (no hydration). The serialization/deserialization logic lives in packages/qwik/src/core/container/.
A QRL (Qwik Resource Locator) is a lazy reference to a closure. Any function ending with $ (e.g., component$, useTask$, $()) creates a QRL boundary — the optimizer extracts these into separate chunks for lazy loading.
- QRL implementation:
packages/qwik/src/core/qrl/ - The Rust optimizer rewrites
$-suffixed calls at build time
Fine-grained reactivity system. Signals track subscriptions and update only the DOM nodes or tasks that read them — no virtual DOM diffing.
- Implementation:
packages/qwik/src/core/state/signal.ts - Stores (proxy-based deep reactivity):
packages/qwik/src/core/state/store.ts
The optimizer is a Rust-based compiler plugin (SWC transform) that:
- Extracts
$-suffixed closures into separate entry points - Captures lexical scope for serialization
- Generates manifest metadata for prefetching
- Rust source:
packages/qwik/src/optimizer/core/ - WASM build:
packages/qwik/src/wasm/ - Native bindings:
packages/qwik/src/napi/
Qwik City provides file-based routing, data loaders (routeLoader$), actions (routeAction$), middleware, and server adapters.
- Runtime:
packages/qwik-city/src/runtime/ - Build tooling:
packages/qwik-city/src/buildtime/ - Adapters:
packages/qwik-city/src/adapters/
Config: Prettier (.prettierrc.json) + ESLint 9 flat config (eslint.config.js)
| Rule | Setting |
|---|---|
| Semi | true |
| Quotes | Single |
| Print width | 100 |
| Tabs | Spaces (2) |
| Trailing comma | ES5 |
no-console |
Error (warn/error allowed) |
curly |
Always required |
| Pattern | Usage | Example |
|---|---|---|
use* |
Hooks (must be called in component or task scope) | useSignal, useStore, useTask$ |
*$ |
QRL boundary — optimizer extracts the closure | component$, routeLoader$ |
create* |
Factory functions | createDOM, createContextId |
*.unit.ts(x) |
Unit test files | qrl.unit.ts |
*.spec.ts |
E2E test files | e2e.events.spec.ts |
- File pattern:
*.unit.ts/*.unit.tsx - Config:
vitest.config.ts(root) - Run all:
pnpm test.unit - Run one:
pnpm vitest run <path> - Server-side DOM helper:
createDOM()from@builder.io/qwik/testing
- File pattern:
*.spec.tsinstarters/e2e/ - Config:
starters/playwright.config.ts - Run:
pnpm test.e2e.chromium - Run one:
pnpm playwright test <path> --project chromium - Additional E2E suites:
e2e/adapters-e2e/,e2e/docs-e2e/,e2e/qwik-react-e2e/
- Run:
pnpm test.rust(ormake test) - Update snapshots:
pnpm test.rust.update(ormake test-update)
type(scope): description
Types: feat, fix, docs, lint, refactor, perf, test, chore
- Use imperative mood ("add feature" not "added feature")
- No trailing period
- Scope is optional but encouraged (e.g.,
fix(qwik-city): ...)
If your change affects published packages, create a changeset:
pnpm changeThis creates a .changeset/*.md file describing the change. The core packages (@builder.io/qwik, @builder.io/qwik-city, eslint-plugin-qwik, create-qwik) are fixed-versioned — they always release together.
- Base branch:
main(trunk-based development) - PRs target
main - CI runs on all PRs
pnpm build.corepnpm test.unit(run relevant tests)pnpm lintpnpm api.update(if you changed public API)pnpm change(to document patches or new features)
- Don't run the full test suite — Use
pnpm test.unitor target specific files. The fullpnpm testruns build + all tests and takes a very long time. - Don't forget
pnpm api.update— If you change any public API, CI will fail without regenerated API docs. - Don't modify Rust code without rebuilding — After touching
packages/qwik/src/optimizer/core/, runpnpm build.full(requires Rust toolchain + wasm-pack). - Don't skip changesets for user-facing changes — CI checks for changesets on PRs that touch published packages.
- Don't commit
.onlytests — ESLint ruleno-only-testsblocks this. - Don't edit generated files — Files in
dist/,lib/, and API docs underpackages/docs/are generated. Edit the source instead.