---
title: "Qwikとは？使い方とベストプラクティスをv1.20.1とv2 betaの実ビルドで解説"
url: "https://www.issoh.co.jp/tech/details/3843/"
published: 2024-10-15
updated: 2026-09-27
categories: ["フレームワーク"]
publisher: "株式会社一創"
---

# Qwikとは？使い方とベストプラクティスをv1.20.1とv2 betaの実ビルドで解説

Qwik（クイック）は、サーバーで描画したHTMLに「どのイベントでどのコードを読み込むか」を書き込み、ブラウザ側でハイドレーションをせずに処理を再開するJavaScriptフレームワークです。2026年9月時点では安定版のv1系（1.20.1）と、パッケージ名から作り直したv2系（2.0.0-beta.45）が並走しています。この記事では両方の版でプロジェクトを実際にビルドし、出力されたHTMLとJavaScriptの中身から、仕組み・使い方・公式ベストプラクティスを確認します。

## まとめ：Qwikの要点（2026年9月時点）

- Qwikの中核はresumability（再開可能性）です。リスナーの位置・コンポーネント境界・状態をSSR時にHTMLへシリアライズし、クライアントでは再実行しません。
- 本番運用の既定は**v1.20.1**（`@builder.io/qwik`、2026-09-23公開）です。`npm create qwik@latest`もv1のプロジェクトを作ります。
- v2は`@qwik.dev/core`と`@qwik.dev/router`に改名済みで、2025-06からbetaのままです。ただし`@qwik.dev/core`の`latest`タグはbeta.45を指すため、パッケージ名で直接入れるとbetaが入ります。
- 「初期JavaScriptゼロ」は正確ではありません。実測では、v1の最小ページでも3,100バイトのqwikloaderを読み込み、コアの50,423バイトを`modulepreload`で先読みしていました。遅らせているのはダウンロードではなく実行です。
- 公式のベストプラクティスは「シグナルの読み取りをテンプレートか`useComputed$`に寄せる」「`useVisibleTask$`は最後の手段」「`window.location`でなく`useLocation()`を使う」が中心です。
- 独自クラスのインスタンスは、そのままではシリアライズできません。実測でもSSGビルドが`Code(3)`（Only primitive and object literals can be serialized）で失敗しました。保持が必要な場合は`noSerialize()`で対象から外します。

以下、仕組み、実測値、版の選び方、使い方、ベストプラクティス、採用判断の順に解説します。

## Qwikの仕組み：ハイドレーションを使わないresumability

QwikはBuilder.ioのGitHub組織（`BuilderIO/qwik`、GitHub APIの作成日は2021-05-19）で開発が始まりました。リポジトリは現在`QwikDev/qwik`に移っており、旧URLからは転送されます。Builder.ioはREADMEで「special sponsor」として表記されています（MITライセンス、GitHub APIで2026-09-27に取得したスター数は22,068）。コンポーネントはJSXとTypeScriptで書くため、見た目はReactに近くなります。ただしページを起動する方法が根本的に異なります。

### ハイドレーションが復元する3つの情報

公式ドキュメントの「[Resumable vs. Hydration](https://qwik.dev/docs/concepts/resumable/)」は、SSRやSSGのアプリがブラウザで起動する際に、フレームワークが次の3つを復元する必要があると説明しています。1つ目はイベントリスナー（どのDOMにどのハンドラーを付けるか）、2つ目はコンポーネントツリー、3つ目はアプリの状態です。ReactやVueはこれをハイドレーションで行います。ハイドレーション対象のコンポーネントでは、対応するコードをダウンロードして実行します。ただし、React Server Componentsのコードはブラウザへ配信されないため、ページ内の全コンポーネントが対象になるわけではありません。レンダリング方式全体の違いは[CSR/SSR/SSGの違いと選定基準](/tech/details/13578/)で整理しています。

Qwikはこの3つをSSR/SSGの時点で集め、HTMLへ書き出します。ブラウザ側では再実行（リプレイ）せず、サーバーで止めた場所から処理を再開します。ハイドレーション自体が無いので、サーバーとクライアントで描画結果が食い違って起きる[ハイドレーションエラー](/tech/details/3218/)とも仕組み上は無縁です。

### $記号・QRL・qwikloaderの役割

Qwikで`component$`や`onClick$`のように末尾へ`$`が付く関数は、Optimizer（ビルド時の変換器）に「ここで別チャンクへ切り出してよい」と伝える目印です。切り出された関数は、チャンクのファイル名とシンボル名を組み合わせたQRL（Qwik URL）で参照されます。HTMLに残るのはコード本体ではなくこのQRLです。

ページ起動時にはqwikloaderが読み込まれ、必要なイベント種別に対応するグローバルリスナーを登録します。ユーザーがボタンを押すと、qwikloaderがイベントの発生元から属性をたどってQRLを読み、該当チャンクだけをダウンロードして実行します。遅延ロードを開発者が設計しなくても、これが既定の動作になります。

## 実測：最小構成のSSG出力に残るJavaScript

「Qwikは初期JavaScriptがゼロ」という説明をよく見かけますが、実際のビルド結果では少し異なります。`create-qwik`の`empty`テンプレートに、カウンター1つとルートローダー1つを置いたページをv1.20.1とv2.0.0-beta.45でSSGビルドし、出力を比べました（2026-09-27、Node.js v26.5.0）。

| 項目           | v1.20.1                | v2.0.0-beta.45          |
| ------------ | ---------------------- | ----------------------- |
| ボタンの属性       | `on:click`             | `q-e:click`             |
| コンテナの状態      | `q:container="paused"` | `q:container="paused"`  |
| 状態のscript    | `qwik/json` 195文字      | `qwik/state` 2,884文字    |
| index.html   | 2,640バイト               | 5,461バイト                |
| qwikloader   | 3,100バイト（gzip 1,678）   | 5,222バイト（gzip 2,386）    |
| 先読みされる最大チャンク | 50,423バイト（gzip 20,345） | 107,112バイト（gzip 38,334） |
| 同梱Vite       | 7.3.1                  | 8.2.1                   |

バイト数は`dist/build/`に出力された配信ファイルの実サイズで、gzipは`gzip -9 -n`で圧縮した値です。v1のボタンは`<button on:click="q-JOju1guk.js#s_fUGjiPat2oI[0]">`として出力されていました。このチャンクは921バイト（gzip 472）ですが、先頭でコアのチャンクを`import`しているため、クリック時の処理にはコアも必要です。HTMLの`<head>`にはコアへの`modulepreload`が入っており、`load`イベント後には`requestIdleCallback`でハンドラーのチャンクも先読みしていました。このビルドでは、ダウンロードはページ表示の直後に済ませ、実行だけをクリックまで遅らせています。回線が細い環境で転送量を見積もるときは、この先読みも含めて計算してください。

[qwikloaderの公式ページ](https://qwik.dev/docs/advanced/qwikloader/)は、サイズを「about 1 kb minified」とし、別の接続を張らずに済むよう`<script>`へのインライン配信を推奨しています。一方、v1.20.1の既定ビルドでは3,100バイトの外部ファイルとして`<script type="module" async src="/build/…">`で読み込まれていました。ドキュメントの数値をそのまま性能予算に使わず、自分のビルド結果で確認してください。

v2 betaでは、公式の移行ガイドが示すとおり属性名が`q-e:`系に変わり、状態は`qwik/state`と`qwik/vnode`の2つのscriptに分かれていました。移行ガイドは「シリアライズされた状態が最大30%小さくなる」と説明していますが、この最小ページではv2の方がHTMLが大きくなっています。削減効果はページ構成によって変わるため、移行前に自分のアプリで測ってから判断するのが確実です。

## v1.20.1とv2 betaの現状と選び方

Qwikの版を確認するときは、パッケージ名が2系統ある点に注意が必要です。npmレジストリのdist-tagsを2026-09-27に取得した結果は次のとおりです。

| パッケージ                   | latestタグ                     | 公開日        |
| ----------------------- | ---------------------------- | ---------- |
| `@builder.io/qwik`      | 1.20.1                       | 2026-09-23 |
| `@builder.io/qwik-city` | 1.20.1                       | 2026-09-23 |
| `@qwik.dev/core`        | 2.0.0-beta.45                | 2026-09-22 |
| `@qwik.dev/router`      | 2.0.0-beta.45                | 2026-09-22 |
| `create-qwik`           | 1.20.1（betaタグは2.0.0-beta.45） | 2026-09-23 |

v2は2024-11-15に2.0.0-alpha.0、2025-06-04に2.0.0-beta.1が公開され、それから1年以上betaが続いています。一方、v1は2026年に入ってからも1.19.0（1月）、1.20.0（5月）、1.20.1（9月）と更新されています。

2026年9月時点の判断基準は明確です。本番で使うなら**v1.20.1で始めてください**。betaは破壊的変更が入り得るうえ、サードパーティのライブラリが`@builder.io/qwik`を前提にしている場合があります。移行ガイドでも、package managerの`overrides`で旧パッケージ名を`npm:@qwik.dev/core@^2`へ差し替える手順を案内しています。v2を試すのは、非同期の`useComputed$`やVite 8対応を検証したい新規の実験プロジェクトに限るのが無難です。

### v1からv2への主な変更点

公式の移行ガイド（[Migrating to Qwik v2](https://next.qwik.dev/docs/upgrade/)）は、v2を「フレームワークの全面的な書き直し」と位置付け、`npx qwik migrate-v2`による自動移行を案内しています。コードに影響する主な変更は次のとおりです。

- インポート先の変更：`@builder.io/qwik`→`@qwik.dev/core`、`@builder.io/qwik-city`→`@qwik.dev/router`（Qwik CityはQwik Routerに改称）
- ルートの`<QwikCityProvider>`を`useQwikRouter()`フックに置き換え
- `useResource$`は非推奨になり、`abortSignal`を受け取れる非同期の`useComputed$`に置き換え
- `useVisibleTask$`の`eagerness`オプションを削除
- 静的出力のアダプタ名が`static`から`ssg`に変更（実測で確認。v2で`qwik add static`を実行すると「Invalid integration」エラーになる）

HTML属性名の変更（`on:click`→`q-e:click`）は、JSXの`onClick$`を書くだけなら影響しません。ただし、E2Eテストやスクレイピングで属性を直接セレクタに使っている場合は壊れます。

## Qwikの使い方：プロジェクト作成からデプロイまで

ここからはv1.20.1を前提に、Qwik City（v2ではQwik Router）でページを作る手順を追います。コードはすべてローカルでビルドし、型チェックとLintが通ることを確認済みです。

### プロジェクト作成とNode.jsの要件

公式の[Getting Started](https://qwik.dev/docs/getting-started/)にはNode.js v18.17以上とありますが、テンプレートが入れるVite 7.3.1（v2 betaではVite 8.2.1）の`engines`はどちらも`^20.19.0 || >=22.12.0`で、実際にはこちらが下限になります。生成される`package.json`の`engines`は`^18.17.0 || ^20.3.0 || >=21.0.0`でした（依存しているsharpのNode-API要件によるもの）。作成コマンドは次のとおりです。

```
npm create qwik@latest
# 対話なしで作る場合（テンプレートと出力先を指定し、-iで依存も入れる）
npx create-qwik@1.20.1 empty my-app -i
cd my-app
npm start
```

テンプレートは`empty`・`playground`・`library`・`e2e-library`の4種類です。v2を試す場合は`npm create qwik@beta`のように`beta`タグを明示します。生成物には`vite.config.ts`と`src/routes/`、`src/entry.ssr.tsx`が含まれます。`npm run dev`で起動する開発サーバーは`vite --mode ssr`で、開発時もSSRで動作します。

### ファイルベースルーティングと状態管理

`src/routes/`配下のディレクトリがURLに対応し、各ディレクトリの`index.tsx`がページになります。`src/routes/about/index.tsx`は`/about/`、`[id]`ディレクトリは動的パラメータです。次のコードは、サーバーで値を取得して表示し、クリックでカウンターを増やすページです。

```
import { component$, useSignal, useComputed$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';

export const useServerTime = routeLoader$(() => {
  return { iso: new Date().toISOString() };
});

export default component$(() => {
  const count = useSignal(0);
  const doubled = useComputed$(() => count.value * 2);
  const time = useServerTime();
  return (
    <main>
      <p>SSR time: {time.value.iso}</p>
      <button onClick$={() => count.value++}>+1</button>
      <p>count: {count.value} / doubled: {doubled.value}</p>
    </main>
  );
});
```

`useSignal`は単一の値を`.value`で読み書きし、`useStore`はオブジェクト全体をリアクティブにします。派生値は`useComputed$`で作ります。シグナルの変更はそれを読んでいるDOMだけを更新するため、Reactのようにコンポーネント関数全体を再実行する必要がありません。Signalベースの設計は[SolidJSのSignal](/tech/details/3841/)と発想が近いですが、Qwikでは状態がHTMLへシリアライズされる点が異なります。

### サーバー処理：routeLoader$・routeAction$・server$

データの取得には`routeLoader$`、フォーム送信などの更新には`routeAction$`を使います。どちらもページの`index.tsx`（または`layout.tsx`）から`export`する必要があります。次の例では、`zod$`で入力を検証します。

```
import { component$ } from '@builder.io/qwik';
import { routeLoader$, routeAction$, zod$, z, Form } from '@builder.io/qwik-city';

const todos: string[] = [];

export const useTodos = routeLoader$(() => todos);

export const useAddTodo = routeAction$(
  (data) => {
    todos.push(data.title);
    return { ok: true };
  },
  zod$({ title: z.string().min(1) })
);

export default component$(() => {
  const list = useTodos();
  const add = useAddTodo();
  return (
    <>
      <Form action={add}>
        <input name="title" />
        <button type="submit">追加</button>
      </Form>
      {add.value?.failed && <p>タイトルは必須です</p>}
      <ul>
        {list.value.map((t, index) => (
          <li key={index}>{t}</li>
        ))}
      </ul>
    </>
  );
});
```

ビルド後のHTMLでは、`<Form>`が`<form action="?qaction=…" method="post">`として出力されていました。JavaScriptが読み込まれる前でも、通常のPOSTとして送信できます。このモジュール変数`todos`は、同じサーバープロセス内の利用者で共有され、再起動すると失われるサンプル用データです。実運用では、利用者ごとのアクセス制御を備えたDBなどに置き換えてください。コンポーネント内から任意のサーバー関数を呼びたい場合は、`server$`で包んだ関数がRPCとして呼び出されます。

### デプロイ：qwik addで追加するアダプタ

作成直後のプロジェクトはアダプタが未設定で、`npm run deploy`を実行しても「`npm run qwik add`でサーバーアダプタを入れる」と表示されるだけです。v1.20.1に同梱されているアダプタは、aws-lambda・azure-swa・bun・cloud-run・cloudflare-pages・cloudflare-workers・deno・express・fastify・firebase・netlify-edge・node-server・static・vercel-edgeの14種類でした。

```
# 直前のフォーム例を公開する場合：サーバー処理に対応するアダプタ
npx qwik add cloudflare-pages
# サーバー処理が不要な静的サイトでは、上のコマンドの代わりに次を実行
# npx qwik add static
npm run build              # 型チェック・Lint・クライアントとサーバーのビルドをまとめて実行
```

`static`アダプタでは`adapters/static/vite.config.ts`が生成され、`origin`の値を実際のドメインに書き換える必要があります。ビルドの最後には、配信時にキャッシュヘッダーを適切に設定するよう促す注意書きが出ます。公式の[Deploymentsページ](https://qwik.dev/docs/deployments/)によれば、`dist/build`と`dist/assets`のファイル名には内容のハッシュが付くため、`Cache-Control: public, max-age=31536000, immutable`で配信するよう推奨されています。

## Qwikのベストプラクティス（公式ガイドの要点）

公式の[Best Practicesページ](https://qwik.dev/docs/guides/best-practices/)（v1とv2で見出しは共通）は、主に「コアの実行とJavaScriptの先行ロードをどう減らすか」について書かれています。ここでは、コードを書くときに判断が必要な4点を取り上げます。

### シグナルの読み取り位置と再実行範囲

Qwikは、シグナルを読んだ関数を、そのシグナルが変わるたびに再実行します。コンポーネント関数の本体で`const doubleCount = count.value * 2`のように読むと、値が変わるたびにテンプレート全体が再描画されます。JSXの中で直接読むか、`useComputed$(() => count.value * 2)`に切り出せば、再実行されるのはその式だけになります。Reactの感覚で「レンダー関数の先頭で値を計算してから返す」書き方をすると、Qwikの細かな更新が効かなくなります。

### useVisibleTask$の使用条件と代替API

`useVisibleTask$`はコンポーネントが画面に入った時点でクライアント側のコードを実行するため、コアの読み込みと実行を強制します。公式は、代わりに次のAPIを使うよう案内しています。サーバーで済む処理には`useTask$`、ルート要素のイベントには`useOn`、`window`や`document`のイベントには`useOnWindow`や`useOnDocument`です。テンプレートに同梱されている`eslint-plugin-qwik`は`qwik/no-use-visible-task`ルールで警告を出します。実際に`window.innerWidth`を読むコードで警告が出ることを確認しました。どうしても必要な場合だけ、`// eslint-disable-next-line qwik/no-use-visible-task`を付けて明示的に使います。

### useLocation()によるURL参照とSSGの制約

URLやクエリパラメータによる分岐を`window.location`で書くと、ブラウザでしか評価できないため、JavaScriptの先行ロードが発生します。`useLocation()`を使えば、同じ分岐をSSRの時点でHTMLとして確定できます。例外はSSGです。ビルド時にはリクエストごとのクエリパラメータが存在しないため、クエリを使う処理はイベントハンドラーの中に移すよう公式は勧めています。

### 独自クラスのシリアライズ制約と除外方法

resumabilityは状態をHTMLへシリアライズできることが前提です。公式の説明によれば、Qwikは循環参照、Date、URL、Map、Set、DOM参照、QRLで包んだクロージャーをシリアライズできます。一方、独自クラスのインスタンスやStreamは、そのままではシリアライズできません。v1でも`noSerialize()`で対象から除外して保持できますが、その値はSSRからブラウザへ引き継がれないため、クライアント側で再初期化する必要があります。`useSignal(new Cart())`のようにクラスのインスタンスを状態へ入れたページをv1.20.1でSSGビルドすると、`QWIK ERROR Code(3)`でビルド全体が失敗しました。v1のコアで定義されているCode(3)の文言は「Only primitive and object literals can be serialized」です。状態はプレーンなオブジェクトと配列で持ち、振る舞いは関数として分けてください。

## React・Vue・Astroとの違いと採用判断

Qwikが有利になるのは、初期表示の速さが売上や検索流入に直結し、かつページ内の操作が多いサイトです。ECの商品一覧や、多数のウィジェットを持つランディングページがこれに当たります。コンテンツ中心でほとんど操作がないサイトであれば、必要な部分だけをハイドレートする[Astro](/tech/details/15369/)で十分です。Astroの位置付けは[AstroとReactの違い](/tech/details/2697/)も参考になります。Vueのエコシステムで同じ課題を解くなら[Nuxt](/tech/details/15790/)、React資産を活かしたフルスタック構成なら[TanStack Start](/tech/details/10179/)が比較対象になります。

| 観点        | Qwik         | React系（Next.js等） | Astro          |
| --------- | ------------ | ---------------- | -------------- |
| 起動方式      | resumability | ハイドレーション         | 部分ハイドレーション     |
| 更新単位      | シグナル単位       | コンポーネント再実行       | 島ごとのUIライブラリ次第  |
| npm週間DL   | 69,344       | —                | 6,543,626      |
| 既存UIライブラリ | Qwik用が中心     | React用を利用可       | React・Vue等を流用可 |

週間ダウンロード数は2026-09-19から25の期間でnpmのAPIから取得しました（`@builder.io/qwik`と`astro`の比較）。v2の`@qwik.dev/core`は同じ期間で4,302件でした。

採用すべきでない条件もはっきりしています。Reactのコンポーネントライブラリを大量に使う管理画面や、ブラウザでしか動かないSDK（地図・リッチエディタ・決済ウィジェットなど）が画面の大半を占めるアプリは、Qwikに向きません。Reactの部品を`@builder.io/qwik-react`の`qwikify$`で包む方法もありますが、包んだ部分は通常のReactとしてハイドレーションされます。こうした部分は`qwikify$`や`useVisibleTask$`で囲うことになり、resumabilityの利点が消えるうえ、状態のシリアライズ制約だけが残ります。加えて、2026年9月時点ではv1からv2への移行が控えています。長期保守を前提とする案件では、移行作業の工数も見積もりに含めてください。

## よくある質問

### Qwikとは何ですか？

Builder.ioで開発が始まったJavaScriptフレームワークです。SSRで生成したHTMLにイベントリスナーの位置と状態を書き込み、ブラウザではハイドレーションをせずに、操作されたときに必要なコードだけを読み込んで実行します。この方式をresumabilityと呼びます。

### Qwikの使い方（始め方）は？

Node.js 20.19以上の20系、または22.12以上の環境で`npm create qwik@1.20.1`を実行し、テンプレート（まずは`empty`）を選びます。`src/routes/`にディレクトリと`index.tsx`を置くとページになり、状態は`useSignal`、サーバーからのデータ取得は`routeLoader$`で書きます。公開する前に`npx qwik add`でデプロイ先のアダプタを追加します。

### Qwikのベストプラクティスは何ですか？

公式ガイドの中心は、シグナルの読み取りをテンプレートか`useComputed$`に寄せること、`useVisibleTask$`を`useTask$`や`useOn`系で置き換えること、`window.location`ではなく`useLocation()`を使うことです。これに加えて、SSRから引き継ぐ状態はシリアライズ可能な値にし、独自クラスなどを保持する場合は`noSerialize()`による除外とクライアント側での再初期化を検討します。

### Qwikの最新バージョンはいくつですか？

2026年9月27日時点で、安定版は2026年9月23日に公開されたv1.20.1です（`@builder.io/qwik`）。v2は`@qwik.dev/core`の2.0.0-beta.45が最新で、まだ正式版になっていません。

### QwikはReactやVueと何が違いますか？

ReactやVueは、ハイドレーションによってブラウザ上でコンポーネントを再実行し、リスナーと状態を復元します。Qwikはこれらをサーバー側でHTMLへ書き出すため、再実行が不要です。書き方はJSXでReactに似ていますが、SSRから引き継ぐ状態はシリアライズ可能な値に限られ、Reactのコンポーネントライブラリはそのままでは使えません。

## 関連記事

- [レンダリングとは？ブラウザ描画の仕組みとCSR/SSR/SSGの違い・選定基準を解説](/tech/details/13578/)
- [SolidJSとは？Reactとの違い・Signalで速い理由・導入手順【2026年版】](/tech/details/3841/)
- [Astroフレームワークとは？特徴と使い方・Next.jsとの使い分けを解説【2026年版】](/tech/details/15369/)
- [ハイドレーションエラー（Hydration Error）とは？原因とReact / Next.jsでの対処法を解説](/tech/details/3218/)
- [TanStack Startとは？フルスタックReactフレームワークの特徴・使い方・デプロイ【2026年版】](/tech/details/10179/)

---

出典: [Qwikとは？使い方とベストプラクティスをv1.20.1とv2 betaの実ビルドで解説](<https://www.issoh.co.jp/tech/details/3843/>)（株式会社一創）
