Receber por email
← Voltar para o catálogo

web-meta-framework-qwik

Qwik resumable framework - zero hydration, $ lazy boundaries, signals, Qwik City file-based routing, routeLoader$, routeAction$, server$ RPC, serialization rules

11estrelas
Atualizado há 7 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add agents-inc/skills

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/web-meta-framework-qwik/svg)](https://www.skillteca.com.br/skills/web-meta-framework-qwik)

Alerta por categoria

Receba novas skills de Design e Frontend toda segunda

1 email curto, só com as skills novas de Design e Frontend. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Qwik Framework Patterns

Quick Guide: Qwik is resumable - it serializes application state on the server and resumes on the client without re-executing framework code (no hydration). Every $ suffix marks a lazy-loading boundary where the optimizer splits code into separate chunks. Only the code for the interaction the user triggers gets downloaded. Use component$ for all components, useSignal/useStore for state, routeLoader$ for server data, routeAction$ for mutations, and server$ for ad-hoc server RPC. The critical mental model: anything crossing a $ boundary must be serializable.

<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST wrap every component in component$() - plain functions cannot be lazy-loaded, cannot use hooks, and cannot use <Slot />)

(You MUST ensure all values captured in a $ closure are serializable - non-serializable captures pass type-checking but fail at runtime)

(You MUST use routeLoader$ for initial server data instead of fetching in useTask$ or useResource$ - loaders run before render and integrate with SSR streaming)

(You MUST use preventdefault:click as a JSX attribute instead of calling event.preventDefault() - event handlers load asynchronously so synchronous Event APIs are unavailable)

(You MUST export routeLoader$ and routeAction$ from route files (index.tsx or layout.tsx in src/routes/) - unexported or misplaced loaders/actions silently do nothing)

(You MUST NOT destructure store properties at the top level - destructuring breaks reactivity because you lose the Proxy reference)

</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

When to use:

  • Building web apps where instant interactivity matters (zero hydration delay)
  • Apps with complex interactivity that would ship too much JS with traditional hydration
  • Projects needing fine-grained lazy loading without manual code-splitting
  • Full-stack apps with server loaders, actions, and RPC via server$
  • Progressive enhancement where forms work without JavaScript

When NOT to use:

  • Static content sites with minimal interactivity (use a static site generator)
  • Projects where the team is deeply invested in React ecosystem libraries that have no Qwik equivalents
  • Apps that rely heavily on non-serializable runtime state (class instances, closures with side effects)

Key patterns covered:

  • Resumability mental model and the $ suffix convention
  • Component definition with component$, props, and <Slot />
  • Reactive state: useSignal, useStore, useComputed$
  • Lifecycle: useTask$, useVisibleTask$, useResource$
  • Event handling: onClick$, preventdefault:click, sync$
  • Qwik City routing: file-based routes, layouts, dynamic params
  • Server data: routeLoader$, routeAction$, server$
  • Serialization rules and the $ boundary

Detailed Resources:

  • For decision frameworks and anti-patterns, see [reference.md](reference.md)

Core patterns:

  • [examples/core.md](examples/core.md) - Components, signals, stores, tasks, events, slots
  • [examples/routing.md](examples/routing.md) - File-based routing, routeLoader$, routeAction$, server$, middleware
  • [examples/serialization.md](examples/serialization.md) - Serialization rules, $ boundary, non-serializable patterns

<philosophy>

Philosophy

Qwik is built on resumability - the idea that the server can serialize the entire application state (component tree, listeners, state) into HTML, and the client can resume exactly where the server left off without re-executing any framework code.

How it differs from hydration frameworks:

Traditional SSR frameworks render HTML on the server, then re-execute all component code on the client to attach event listeners and rebuild the component tree. This is hydration - the client replays the server's work.

Qwik skips this entirely. The server serializes everything into the HTML. When a user clicks a button, only the click handler's code downloads and executes. The framework itself, the component tree, and all other handlers stay unloaded until needed.

The $ suffix is the core mechanism. Every function ending in $ is a lazy-loading boundary. The Qwik optimizer splits code at each $ marker into separate chunks. This means:

  • component$() - the component's render function loads only when needed
  • onClick$() - the click handler loads only when the user clicks
  • routeLoader$() - the loader runs server-side only
  • useTask$() - the task loads when its tracked dependencies change

The tradeoff: Because code must be serializable to cross $ boundaries, you cannot capture non-serializable values (class instances, functions, DOM nodes) in $ closures. This constraint is the price of instant interactivity.

When to use Qwik:

  • Interactive apps where time-to-interactive matters
  • Large apps where traditional hydration downloads too much JS upfront
  • Full-stack apps leveraging routeLoader$/routeAction$/server$ for server logic
  • Progressive enhancement (Qwik forms work without JS)

When NOT to use Qwik:

  • Static content sites with little interactivity
  • Projects heavily dependent on React-specific libraries without Qwik equivalents
  • Apps requiring extensive non-serializable runtime state

</philosophy>

<patterns>

Core Patterns

Pattern 1: Components with component$

Every Qwik component must be wrapped in component$(). This is not optional - it enables lazy loading, hooks, and <Slot />.

import { component$, useSignal } from "@builder.io/qwik";

interface CounterProps {
  initial?: number;
  label: string;
}

export const Counter = component$<CounterProps>(({ initial = 0, label }) => {
  const count = useSignal(initial);

  return (
    <div>
      <span>
        {label}: {count.value}
      </span>
      <button onClick$={() => count.value++}>+</button>
    </div>
  );
});

Why good: component$ enables the optimizer to split this into a lazy chunk, typed props via generic, useSignal for reactive state, onClick$ handler loads only on click

// BAD: Plain function component
export const Counter = (props: { label: string }) => {
  // Cannot use hooks here - useSignal will throw
  // Cannot use <Slot /> - only works inside component$
  return <div>{props.label}</div>;
};

Why bad: Without component$ wrapper, hooks throw at runtime, <Slot /> breaks, optimizer cannot split the code, component is not resumable

Pattern 2: Reactive State - useSignal vs useStore

useSignal holds a single reactive value accessed via .value. useStore holds a reactive object with deep tracking by default.

import { component$, useSignal, useStore } from "@builder.io/qwik";

export const UserProfile = component$(() => {
  // useSignal for primitives and flat values
  const isEditing = useSignal(false);
  const selectedTab = useSignal<"profile" | "settings">("profile");

  // useStore for objects/arrays - deep reactivity by default
  const user = useStore({
    name: "Alice",
    email: "alice@example.com",
    preferences: {
      theme: "dark",
      notifications: true,
    },
  });

  return (
    <div>
      <h1>{user.name}</h1>
      {isEditing.value ? (
        <input
          value={user.name}
          onInput$={(_, el) => {
            user.name = el.value;
          }}
        />
      ) : (
        <button
          onClick$={() => {
            isEditing.value = true;
          }}
    
Compartilhar X LinkedIn