qwik
Qwikは、サーバーでアプリケーションの状態を保存し、必要な時にJavaScriptを読み込むことで、高速なウェブサイト表示を実現し、エッジ環境への展開にも適したウェブフレームワークを提供するSkill。
📜 元の英語説明(参考)
Qwik is a resumable web framework that delivers instant-loading applications by eliminating hydration. It serializes application state on the server and lazily loads JavaScript on interaction, making it ideal for edge deployment.
🇯🇵 日本人クリエイター向け解説
Qwikは、サーバーでアプリケーションの状態を保存し、必要な時にJavaScriptを読み込むことで、高速なウェブサイト表示を実現し、エッジ環境への展開にも適したウェブフレームワークを提供するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o qwik.zip https://jpskill.com/download/15310.zip && unzip -o qwik.zip && rm qwik.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/15310.zip -OutFile "$d\qwik.zip"; Expand-Archive "$d\qwik.zip" -DestinationPath $d -Force; ri "$d\qwik.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
qwik.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
qwikフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Qwik
Qwik は、アプリケーションの状態を HTML にシリアライズすることで、ハイドレーションを排除します。JavaScript はページロード時ではなく、ユーザーインタラクション時に遅延ロードされます。これは、アプリケーションの複雑さに関わらず、初期ロード時の JS がほぼゼロになることを意味します。
インストール
# Qwik City (メタフレームワーク) を使用して Qwik プロジェクトを作成
npm create qwik@latest
cd my-app
npm install
npm run dev
プロジェクト構成
# Qwik City プロジェクトのレイアウト
src/
├── entry.ssr.tsx # SSR エントリー
├── root.tsx # ルートコンポーネント
├── global.css
├── routes/ # ファイルベースルーティング
│ ├── layout.tsx # ルートレイアウト
│ ├── index.tsx # / ページ
│ └── articles/
│ ├── index.tsx # /articles
│ └── [slug]/
│ └── index.tsx # /articles/:slug
├── components/ # 再利用可能なコンポーネント
│ └── article-card/
│ └── article-card.tsx
└── lib/ # ユーティリティ
コンポーネント
// src/components/article-card/article-card.tsx — Qwik コンポーネント
import { component$ } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';
interface Props {
title: string;
slug: string;
excerpt: string;
}
export const ArticleCard = component$<Props>((props) => {
return (
<article>
<Link href={`/articles/${props.slug}`}>
<h2>{props.title}</h2>
</Link>
<p>{props.excerpt}</p>
</article>
);
});
シグナルと状態
// src/routes/counter/index.tsx — シグナルとリアクティビティ
import { component$, useSignal, useComputed$, useTask$ } from '@builder.io/qwik';
export default component$(() => {
const count = useSignal(0);
const doubled = useComputed$(() => count.value * 2);
useTask$(({ track }) => {
track(() => count.value);
console.log(`Count changed to ${count.value}`);
});
return (
<div>
<p>Count: {count.value} (doubled: {doubled.value})</p>
<button onClick$={() => count.value++}>+1</button>
</div>
);
});
routeLoader$ を使用したデータローディング
// src/routes/articles/index.tsx — サーバーサイドデータローディング
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
import { ArticleCard } from '~/components/article-card/article-card';
export const useArticles = routeLoader$(async ({ env }) => {
const res = await fetch(`${env.get('API_URL')}/articles`);
return res.json() as Promise<Article[]>;
});
export default component$(() => {
const articles = useArticles();
return (
<div>
<h1>Articles</h1>
{articles.value.map((article) => (
<ArticleCard key={article.id} title={article.title} slug={article.slug} excerpt={article.excerpt} />
))}
</div>
);
});
サーバーアクション
// src/routes/articles/new/index.tsx — サーバーアクションを持つフォーム
import { component$ } from '@builder.io/qwik';
import { routeAction$, Form, zod$, z } from '@builder.io/qwik-city';
export const useCreateArticle = routeAction$(
async (data, { redirect, env }) => {
const res = await fetch(`${env.get('API_URL')}/articles`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) return { success: false, error: 'Failed to create' };
throw redirect(302, '/articles');
},
zod$({
title: z.string().min(1).max(200),
body: z.string().min(1),
})
);
export default component$(() => {
const action = useCreateArticle();
return (
<Form action={action}>
<input name="title" placeholder="Title" required />
<textarea name="body" placeholder="Body" required />
<button type="submit">Create</button>
{action.value?.error && <p>{action.value.error}</p>}
</Form>
);
});
レイアウト
// src/routes/layout.tsx — ルートレイアウト
import { component$, Slot } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';
export default component$(() => {
return (
<div>
<header>
<nav>
<Link href="/">Home</Link>
<Link href="/articles">Articles</Link>
</nav>
</header>
<main>
<Slot />
</main>
</div>
);
});
ミドルウェア
// src/routes/admin/layout.tsx — onRequest を介した認証ミドルウェア
import { type RequestHandler } from '@builder.io/qwik-city';
export const onRequest: RequestHandler = async ({ cookie, redirect }) => {
const token = cookie.get('session')?.value;
if (!token) throw redirect(302, '/login');
};
複雑な状態のための useStore
// src/routes/dashboard/index.tsx — ネストされたリアクティブな状態のためのストア
import { component$, useStore, $ } from '@builder.io/qwik';
export default component$(() => {
const state = useStore({
articles: [] as Article[],
filter: '',
loading: false,
});
const fetchArticles = $(async () => {
state.loading = true;
const res = await fetch('/api/articles');
state.articles = await res.json();
state.loading = false;
});
return (
<div>
<button onClick$={fetchArticles}>Load</button>
<input bind:value={state.filter} placeholder="Filter..." />
{state.loading ? <p>Loading...</p> : (
state.articles
.filter((a) => a.title.includes(state.filter))
.map((a) => <div key={a.id}>{a.title}</div>)
)}
</div>
);
});
デプロイメント
# デプロイメントアダプターを追加
npm run qwik add cloudflare-pages # または: vercel, netlify, node-server, deno
npm run build
npm run deploy
主要なパターン
- 関数に
$サフィックスを使用する (onClick$,component$,routeLoader$) — これはシリアライゼーションの境界を示します - プリミティブには
useSignal、オブジェクトにはuseStoreを使用する — Solid の signal/store の分割に似ています routeLoader$は SSR 中にサーバー上で実行される — データは HTML にシリアライズされますrouteAction$は Zod バリデーションを使用してサーバーサイドでフォームの送信を処理します- JavaScript はインタラクションごとに遅延ロードされる — ハイドレーションステップはありません
- サーバーサイドミドルウェア (認証、リダイレクト) にはレイアウトで
onRequestハンドラーを使用する - デプロイ
(原文はここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Qwik
Qwik eliminates hydration by serializing the application state into HTML. JavaScript loads lazily on user interaction, not on page load. This means near-zero JS on initial load regardless of app complexity.
Installation
# Create Qwik project with Qwik City (meta-framework)
npm create qwik@latest
cd my-app
npm install
npm run dev
Project Structure
# Qwik City project layout
src/
├── entry.ssr.tsx # SSR entry
├── root.tsx # Root component
├── global.css
├── routes/ # File-based routing
│ ├── layout.tsx # Root layout
│ ├── index.tsx # / page
│ └── articles/
│ ├── index.tsx # /articles
│ └── [slug]/
│ └── index.tsx # /articles/:slug
├── components/ # Reusable components
│ └── article-card/
│ └── article-card.tsx
└── lib/ # Utilities
Components
// src/components/article-card/article-card.tsx — Qwik component
import { component$ } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';
interface Props {
title: string;
slug: string;
excerpt: string;
}
export const ArticleCard = component$<Props>((props) => {
return (
<article>
<Link href={`/articles/${props.slug}`}>
<h2>{props.title}</h2>
</Link>
<p>{props.excerpt}</p>
</article>
);
});
Signals and State
// src/routes/counter/index.tsx — signals and reactivity
import { component$, useSignal, useComputed$, useTask$ } from '@builder.io/qwik';
export default component$(() => {
const count = useSignal(0);
const doubled = useComputed$(() => count.value * 2);
useTask$(({ track }) => {
track(() => count.value);
console.log(`Count changed to ${count.value}`);
});
return (
<div>
<p>Count: {count.value} (doubled: {doubled.value})</p>
<button onClick$={() => count.value++}>+1</button>
</div>
);
});
Data Loading with routeLoader$
// src/routes/articles/index.tsx — server-side data loading
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
import { ArticleCard } from '~/components/article-card/article-card';
export const useArticles = routeLoader$(async ({ env }) => {
const res = await fetch(`${env.get('API_URL')}/articles`);
return res.json() as Promise<Article[]>;
});
export default component$(() => {
const articles = useArticles();
return (
<div>
<h1>Articles</h1>
{articles.value.map((article) => (
<ArticleCard key={article.id} title={article.title} slug={article.slug} excerpt={article.excerpt} />
))}
</div>
);
});
Server Actions
// src/routes/articles/new/index.tsx — form with server action
import { component$ } from '@builder.io/qwik';
import { routeAction$, Form, zod$, z } from '@builder.io/qwik-city';
export const useCreateArticle = routeAction$(
async (data, { redirect, env }) => {
const res = await fetch(`${env.get('API_URL')}/articles`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) return { success: false, error: 'Failed to create' };
throw redirect(302, '/articles');
},
zod$({
title: z.string().min(1).max(200),
body: z.string().min(1),
})
);
export default component$(() => {
const action = useCreateArticle();
return (
<Form action={action}>
<input name="title" placeholder="Title" required />
<textarea name="body" placeholder="Body" required />
<button type="submit">Create</button>
{action.value?.error && <p>{action.value.error}</p>}
</Form>
);
});
Layouts
// src/routes/layout.tsx — root layout
import { component$, Slot } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';
export default component$(() => {
return (
<div>
<header>
<nav>
<Link href="/">Home</Link>
<Link href="/articles">Articles</Link>
</nav>
</header>
<main>
<Slot />
</main>
</div>
);
});
Middleware
// src/routes/admin/layout.tsx — auth middleware via onRequest
import { type RequestHandler } from '@builder.io/qwik-city';
export const onRequest: RequestHandler = async ({ cookie, redirect }) => {
const token = cookie.get('session')?.value;
if (!token) throw redirect(302, '/login');
};
useStore for Complex State
// src/routes/dashboard/index.tsx — store for nested reactive state
import { component$, useStore, $ } from '@builder.io/qwik';
export default component$(() => {
const state = useStore({
articles: [] as Article[],
filter: '',
loading: false,
});
const fetchArticles = $(async () => {
state.loading = true;
const res = await fetch('/api/articles');
state.articles = await res.json();
state.loading = false;
});
return (
<div>
<button onClick$={fetchArticles}>Load</button>
<input bind:value={state.filter} placeholder="Filter..." />
{state.loading ? <p>Loading...</p> : (
state.articles
.filter((a) => a.title.includes(state.filter))
.map((a) => <div key={a.id}>{a.title}</div>)
)}
</div>
);
});
Deployment
# Add deployment adapter
npm run qwik add cloudflare-pages # or: vercel, netlify, node-server, deno
npm run build
npm run deploy
Key Patterns
- Use
$suffix on functions (onClick$,component$,routeLoader$) — it marks serialization boundaries - Use
useSignalfor primitives,useStorefor objects — similar to Solid's signal/store split routeLoader$runs on the server during SSR — data is serialized into HTMLrouteAction$handles form submissions server-side with Zod validation- JavaScript loads lazily per interaction — no hydration step
- Use
onRequesthandlers in layouts for server-side middleware (auth, redirects) - Deploy anywhere: Cloudflare, Vercel, Netlify, Deno, Node — via adapters