web-meta-framework-qwik-ab7ccc — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited web-meta-framework-qwik-ab7ccc (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Quick Guide: Qwik is resumable rather than hydrated — the server serializes application state into the HTML and the client picks it up without re-executing framework code. Every$suffix marks a lazy-loading boundary the optimizer splits into its own chunk, so only the code for the interaction a user actually triggers is downloaded.component$wraps every component,useSignal/useStorehold state,routeLoader$supplies server data,routeAction$handles mutations, andserver$is ad-hoc RPC. The constraint everything else follows from: anything captured across a$boundary must be serializable.
Detailed Resources:
server$, endpoints, middleware, navigation$ boundary, noSerialize, lean closures, QRL propsThe two packages divide the surface, and mixing up which one an export comes from is the commonest import error.
@builder.io/qwik. Follow examples/core.md.
server$, endpoints and middleware,all from @builder.io/qwik-city. Follow examples/routing.md.
<critical_requirements>
Wrap every component in `component$()`. A plain function cannot be lazy-loaded, cannot call hooks and cannot host <Slot />.
Keep everything captured in a `$` closure serializable. A non-serializable capture type-checks and then fails at runtime, which is why the compiler is no help here.
Reach for `routeLoader$` for initial server data rather than fetching in `useTask$` or `useResource$`. Loaders run before render and integrate with SSR streaming, so there is no loading state to show.
Cancel default behaviour with the `preventdefault:click` JSX attribute rather than `event.preventDefault()`. Handlers load asynchronously, so the synchronous Event APIs have already had their effect by the time the handler runs.
Export `routeLoader$` and `routeAction$` from a route file — index.tsx or layout.tsx under src/routes/. Anywhere else, or unexported, they silently do nothing.
Read and write store properties through the store reference — `store.name`. Destructuring extracts the value from the Proxy, and reactivity goes with it.
</critical_requirements>
Auto-detection: Qwik, component$, useSignal, useStore, useTask$, useVisibleTask$, useComputed$, useResource$, routeLoader$, routeAction$, server$, sync$, QRL, noSerialize, @builder.io/qwik, @builder.io/qwik-city, Qwik City, $(), onClick$, onInput$, Slot, q:slot, preventdefault, stoppropagation, useStylesScoped$, resumable, resumability
Applies to:
$ suffix, and what the optimizer does with itcomponent$, typed props, QRL callback props, <Slot /> projectionuseSignal, useStore, useComputed$useTask$, useVisibleTask$, useResource$on{Event}$, preventdefault:/stoppropagation: attributes, sync$, global listenersrouteLoader$, routeAction$, server$, endpoint handlers, onRequest middlewareHandled elsewhere:
zod$() wraps — the action integration is Qwik's, the schema language is not.server$ or a loader — those functions are just server code.<philosophy>
Qwik is built on resumability. A hydrating framework renders HTML on the server and then re-executes every component on the client to reattach listeners and rebuild the tree. Qwik does not: the server serializes the tree, the state and the listeners into the HTML, and the client resumes from there. When a user clicks a button, that handler's chunk is what downloads — not the framework, not the tree, not the other handlers.
The `$` suffix is the mechanism. Each $ is a split point the optimizer turns into a separately loadable chunk: component$() for a render function, onClick$() for a handler, useTask$() for a tracked effect, routeLoader$() for server-only code.
The price is serialization. A chunk that loads later needs its captured scope restored from the HTML, so a $ closure can only close over values Qwik knows how to write down. Class instances, functions and DOM nodes are not among them, and that single constraint explains most of the API's unfamiliar corners — QRL props, noSerialize, the advice to keep closures lean.
</philosophy>
<decision_framework>
Which state primitive? A single primitive is useSignal, read and written through .value. An object or array is useStore, mutated property by property with deep tracking on by default. A value derived synchronously from others is useComputed$. A value derived asynchronously is useResource$. Data that comes from the server is routeLoader$.
Which lifecycle hook? useTask$ is the default; the table in Pattern 4 has the rest. useVisibleTask$ defeats resumability, so it is for DOM measurement, browser-only APIs and canvas work and nothing else.
Where should data loading live? Needed before the page renders: routeLoader$. Reactive to client state: useResource$, calling server$ when the work belongs on the server. Triggered by the user: routeAction$ for anything form-shaped, server$ called from a handler when there is no form.
Does the handler need a synchronous Event API? preventDefault becomes the preventdefault:eventname attribute, stopPropagation becomes stoppropagation:eventname, and currentTarget becomes the handler's second parameter. Everything else is an ordinary on{Event}$, extracted with $() and typed as QRL when it is reused.
Which styling attachment? useStylesScoped$ scopes styles to the component and lazy-loads them with it, with :global() as the escape hatch for projected <Slot /> content. useStyles$ attaches unscoped styles the same way. Anything site-wide is imported in the root layout. Runtime CSS-in-JS that injects styles during render is incompatible with SSR streaming here; zero-runtime approaches are not.
</decision_framework>
<patterns>
component$Every component is wrapped, and the wrapper is what gives it lazy loading, hooks and <Slot />.
export const Counter = component$<{ initial?: number; label: string }>(
({ initial = 0, label }) => {
const count = useSignal(initial);
return (
<button onClick$={() => count.value++}>
{label}: {count.value}
</button>
);
},
);Full code: examples/core.md
useSignal and useStoreuseSignal holds one value behind .value. useStore holds an object and tracks it deeply, mutated in place — but only through the store reference.
const isEditing = useSignal(false);
const user = useStore({ name: "Alice", preferences: { theme: "dark" } });
user.name = "Bob"; // reactive
const { name } = user; // plain string — reactivity lostFull code: examples/core.md
useComputed$Synchronous derivation with automatic dependency tracking and no dependency array. The result is a read-only signal.
const subtotal = useSignal(85);
const tax = useComputed$(() => subtotal.value * TAX_RATE);
const total = useComputed$(() => subtotal.value + tax.value);Full code: examples/core.md
track() declares what re-runs the task; cleanup() runs before each re-run and on teardown.
useTask$(({ track, cleanup }) => {
const term = track(() => query.value);
const timer = setTimeout(() => search(term), DEBOUNCE_MS);
cleanup(() => clearTimeout(timer));
});| Hook | Runs | Use for |
|---|---|---|
useTask$ | Server + client, before render | Data init, side effects on state change |
useVisibleTask$ | Browser only, after render | DOM manipulation, browser APIs, animations |
useComputed$ | Synchronous, auto-tracked | Derived values (formatting, filtering) |
useResource$ | Server + client, non-blocking | Async data that should not block render |
A useTask$ that tracks nothing runs once, as an initialization hook rather than a reactive effect.
Full code: examples/core.md
Handlers are on{Event}$ and load asynchronously, so default-behaviour control is declarative and the element arrives as the second parameter instead of through currentTarget.
<form preventdefault:submit onSubmit$={handleSubmit}>
<input value={email.value} onInput$={(_, el) => (email.value = el.value)} />
</form>Full code: examples/core.md
<Slot /><Slot /> takes children; q:slot on a direct child of the usage site routes into a named slot. Both work only inside component$().
<div class="card">
<header>
<Slot name="header" />
</header>
<div class="body">
<Slot />
</div>
</div>Wrapping slotted content in an intermediate element breaks the projection — q:slot must sit on a direct child.
Full code: examples/core.md
routeLoader$ for server dataExported from a route file, runs server-side before render, and returns a read-only signal. fail() returns a typed error rather than throwing.
export const useProduct = routeLoader$(async (requestEvent) => {
const product = await db.products.findById(requestEvent.params.id);
return product ?? requestEvent.fail(404, { errorMessage: "Not found" });
});
export default component$(() => {
const product = useProduct();
return <h1>{product.value.name}</h1>;
});Full code: examples/routing.md
routeAction$ for mutationsHandles form submissions server-side, with zod$() for validation and per-field errors. <Form> works without JavaScript, so the page degrades to a plain POST.
export const useContactAction = routeAction$(
async (data) => {
await sendEmail(data);
return { success: true };
},
zod$({ email: z.string().email(), message: z.string().min(10) }),
);<Form action={action}>
{action.value?.fieldErrors?.email && <p>{action.value.fieldErrors.email}</p>}
<button type="submit" disabled={action.isRunning}>
Send
</button>
</Form>Full code: examples/routing.md
</patterns>
<red_flags>
Breaks at runtime:
<Slot /> silently fails, and the optimizer cannot split it.$ closure — type-checks, then throws a serialization error.const { name } = store yields a plain value, and every later write is invisible to the UI.event.preventDefault() or event.stopPropagation() inside a handler — a no-op, because the handler ran too late. Use the preventdefault: / stoppropagation: attributes.event.currentTarget inside a handler — null in an async handler; take the element from the second parameter.routeLoader$ or routeAction$ outside src/routes/**/index.tsx or layout.tsx, or defined but not exported — no error, and no execution either.QRL<() => void> and their values are wrapped in $().@builder.io/qwik; routing, loaders, actions and server$ are @builder.io/qwik-city.Surprising behaviour:
useVisibleTask$ where useTask$ would do gives up SSR and resumability for that component.useTask$ blocks the render until it settles, so long async work belongs in useResource$.useTask$ instead of routeLoader$ costs SSR streaming and adds a loading state the user should never have seen.useStore({ deep: true }) is the default written out; { deep: false } is the option that changes anything.this — use function () {}.<style> tag in a component double-loads, once from SSR and once from the client.useStylesScoped$ scopes through emoji-based class selectors, which some CSS parsers and test tools mishandle; :global() is how you reach <Slot /> content through it.layout.tsx does not run for server$ calls — put checks a server$ call must pass in plugin.ts or inside the function.server$ needs client and server on the same deployed version; a stale client calling a moved function is undefined behaviour.Signal when the child must write back.store[key] does not track store[key].nested.</red_flags>
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.