sveltekit について
開発者やコーディングエージェント向けに、SvelteKit のアプリケーション設計を案内するスキルです。サーバー境界、データの所有権、セキュリティ、プログレッシブエンハンスメントを重視します。
主な機能
- 01ルートと実行環境の境界
- 02サーバー側のデータ読み込み
- 03ネイティブフォームとアクション
- 04明示的な認証チェック
- 05SSRとハイドレーションの指針
活用シーン
- 01適切なSvelteKitルートファイルの選択
- 02サーバーデータと秘密情報の流れの確認
- 03JavaScriptなしでも動くフォームの構築
- 04ページとエンドポイントの認証確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ joshuadavidthomas/ agent-skills --skill sveltekitインストール後、Claude Code で/sveltekitを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdreferences/auth.mdbetter-auth.mdcloudflare.mderrors-and-redirects.mdfile-naming.mdform-actions.mdforms-validation.mdlayout-patterns.mdload-functions.mdremote-functions.mdserialization.mdssr-hydration.md詳細
SvelteKit の考え方
SvelteKit は、フォルダーを使っただけの Svelte ではありません。アプリケーションの境界を定義するシステムです。ルートファイルがデータの所有者を定め、サーバーコードはサーバー上にとどまり、フォーム送信は段階的に強化され、リダイレクトやエラーは制御フローの一部となります。
主な失敗パターンは、SvelteKit を、たまたまサーバー側のヘルパーを備えたクライアントサイド SPA のように扱うことです。コンポーネントからサーバー所有のデータを取得する。認証をフックに隠し、レイアウトがエンドポイントも保護すると想定する。load からクラスインスタンスを返す。redirect() をスローせずに呼び出す。ネイティブフォームとアクションで実現できるのに、JavaScript だけで動作するフォームを構築する。これらは、SSR、段階的拡張、キャッシュ、セキュリティ境界が重要になるまでは機能します。
アプリケーションレベルの SvelteKit に関する判断には、このスキルを使用してください。コンポーネント内部の処理(runes、snippets、コンポーネントの props、DOM イベント、アクセシビリティ、トランジション、コンポーネントの状態)には svelte5 を使用してください。
SvelteKit の考え方
ルートがアプリケーションの境界を定める
1. ファイル名が動作を決めます。 +page.svelte、+layout.server.ts、+page.server.ts、+server.ts は単なる整理用の情報ではありません。コードの実行場所と呼び出せる主体を決めます。references/file-naming.md を参照してください。
2. ルートグループは URL ではなくポリシーを整理します。 (app) と (auth) は、パスを変えずにレイアウト、認証、データの境界を定めるために使います。保護されたルートは保護されたレイアウトの下に置き、ログイン/サインアップはそのグループの外に置いてください。references/layout-patterns.md を参照してください。
3. レイアウトが保護するのはページであり、エンドポイントではありません。 保護された +layout.server.ts は子ページへのアクセスを制御します。ただし、兄弟または子の +server.ts エンドポイントは保護しません。各エンドポイントで認証を必ず独自に適用してください。references/auth.md を参照してください。
データフローで所有者を明確にする
4. サーバー所有のデータはサーバーで読み込みます。 データベースクエリ、シークレット、プライベート API、セッションに基づくデータは、コンポーネントの onMount やブラウザーからの fetch ではなく、+page.server.ts/+layout.server.ts に置きます。references/load-functions.md を参照してください。
// ❌ component fetches server-owned data after mount
onMount(async () => { user = await (await fetch('/api/me')).json(); });
// ✅ +page.server.ts — server owns it, page receives data
export const load = async ({ locals }) => ({ user: await locals.getUser() });
5. ユニバーサル load は、どちらでも実行できる処理に使います。 +page.ts は、ブラウザーとサーバーの両方で安全に実行できるコードに限って使用してください。シークレットや信頼できる認証情報が必要なら、それはユニバーサルではありません。
6. load が返すのはデータであり、振る舞いではありません。 サーバーの load からは、シリアライズ可能なデータを返します。クラスインスタンス、関数、データベースハンドル、または同一性に依存する複雑なオブジェクトは返さないでください。references/serialization.md を参照してください。
7. リダイレクトとエラーはスローする制御フローです。 SvelteKit 2 では、redirect() と error() はオブジェクトを返します。それらをスローしてください。呼び出すだけではバグになります。references/errors-and-redirects.md を参照してください。
// ❌ bare call — execution continues, the redirect never happens
if (!locals.user) redirect(303, '/login');
// ✅ throw it
if (!locals.user) throw redirect(303, '/login');
フォームはサーバー優先です
8. フォームアクションをデフォルトの変更処理境界とします。 ユーザーがフォーム形式のデータを送信する場合、クライアント専用の変更処理を独自に作る前に、ネイティブフォームと +page.server.ts のアクションを優先してください。references/form-actions.md を参照してください。
9. 段階的拡張はあくまで拡張です。 フォームは JavaScript がなくても動作する必要があります。use:enhance は、処理中の状態、フォーカス、無効化、UX を改善します。変更処理を動作させる唯一の方法にしてはいけません。
10. 検証は境界で行います。 サーバーで検証し、fail を使ってフィールド形式のエラーを返し、コンポーネントで表示します。プロジェクトで shadcn-svelte または bits-ui を使用している場合は、場当たり的なラッパーではなく、Field.* コンポーネントを通してエラーを表示してください。references/forms-validation.md を参照してください。
// +page.server.ts action — validate server-side, return field-shaped errors
if (!email) return fail(400, { errors: { email: 'Email is required' } });
認証は明示的に、シンプルに
11. フックはコンテキストを設定し、ルートはポリシーを適用します。 hooks.server.ts ではセッションを解析して locals に設定します。レイアウトやサーバーのロード処理、エンドポイントで許可する内容を決定します。インフラストラクチャが利用できない場合に、認証を黙って省略してはいけません。references/auth.md を参照してください。
12. Cookie と locals はリクエストの状態です。 リクエストごとのサーバーデータとして扱います。グローバルなモジュール状態に複製したり、認可のためにクライアント側のコピーを信頼したりしてはいけません。
13. API エンドポイントはそれぞれ独自のセキュリティ境界です。 各 +server.ts 内でメソッド、認証、認可、入力検証、レスポンスの形式を確認します。ページレイアウトは直接の HTTP 呼び出しには関係ありません。
リモート関数もサーバー境界です
14. リモート関数を使っても検証は不要になりません。 command、query、form は、いずれもクライアントとサーバーの境界を越えます。引数を検証し、認証を確認して、シリアライズ可能なデータを返します。リモート関数はバージョンや機能フラグに依存します。推奨する前に、プロジェクトで有効化されていることを確認してください。references/remote-functions.md を参照してください。
15. 意図に応じてリモートプリミティブを選びます。 繰り返し行う読み取りには query、命令的な変更には command、フォーム形式の送信には form を使用します。理由もなく、SvelteKit の通常のページ/フォームモデルを迂回するためにリモート関数を使ってはいけません。
SSR がデフォルトの環境です
16. ブラウザー API は条件付きで使用します。 window、document、localStorage、メディア API、DOM の計測は SSR 中には利用できません。コンポーネントのエフェクト、browser、またはクライアント専用の境界を意図的に使用してください。references/ssr-hydration.md を参照してください。
17. ハイドレーションの不一致は設計へのフィードバックです。 サーバーとクライアントで異なる初期マークアップがレンダリングされる場合は、データ境界を修正するか、ブラウザー専用のレンダリングを意図的に遅らせてください。ランダムなクライアント側チェックで不一致を取り繕ってはいけません。
変更に取り組む
外側から内側へ進めます。次の境界に進む前に、それぞれを確定してください。
- ランタイムとルート境界。 存在する
+ファイルと、それぞれが実行される場所(サーバー/ユニバーサル/エンドポイント)を特定します。→ シークレットと DB アクセスが+page.tsやコンポーネントに渡らないことを確認します。 - データの所有者。 サーバーが所有するデータは
+*.server.tsに置き、loadはシリアライズ可能なデータを返します。→ クラスインスタンスや関数がloadを越えて渡されないことを確認します。 - 変更処理。 フォーム形式のデータは、ネイティブフォームと
+page.server.tsのアクションを通じて処理します。→use:enhanceを追加する前に、JavaScript を無効にしても動作することを確認します。 - 認証。 フックは
localsを設定し、各ページのロード処理と各+server.tsがポリシーを適用して、失敗時はアクセスを拒否します。→ ページだけでなく、各エンドポイントへの直接 HTTP 呼び出しもテストします。 - SSR。 ブラウザー API はゲートし、初期マークアップを一致させます。→ JS を無効にしてレンダリングし、ハイドレーションの不一致がないことを確認します。
- 指摘事項の順序: まずセキュリティと正確性(認証、シリアライズ、スローされる制御フロー)、次に段階的な拡張の不足、最後に使いやすさを確認します。
フォームアクションの流れ
- 実際の入力欄とラベルを備えたネイティブな
<form method="POST">。 +page.server.tsのactionsでサーバー側の検証を行います。入力に問題があればreturn fail(400, { errors })、成功したらthrow redirect(...)を実行します。- コンポーネントで
form?.errorsをレンダリングします(プロジェクトで shadcn-svelte / bits-ui を使用している場合はField.*を通じて表示します)。 - 最後に
use:enhanceを追加します。保留中の状態、フォーカス、無効化処理にのみ使用します。 - 確認: JavaScript を無効にして送信しても、変更処理が機能することを確認します。
ルートの保護
- 保護されたルートは、保護された
+layout.server.tsを持つグループ内に配置します。ログイン/サインアップはその外に置きます。 hooks.server.tsでセッションを解析し、localsに設定します。- 保護された
+layout.server.tsで、未認証ユーザーに対してthrow redirect(...)を実行します。 - 保護対象の各
+server.tsで、それぞれ認証を再確認します。 - 確認: セッションなしで保護対象のエンドポイントに直接リクエストした場合、拒否されることを確認します。
よくある間違い(エージェントの失敗パターン)
onMountでサーバー所有のデータを取得する → サーバーのloadを使います。$effectを使ってクエリ/ページの状態をナビゲーションに反映する → コントロールのイベントハンドラーまたはフォーム送信で URL を更新します。必要に応じて、表示値はdataから導出します。- シークレットにユニバーサルロードを使う →
+page.server.tsまたは+layout.server.tsに移します。 loadからクラス/関数を返す → シリアライズ可能なプレーンデータを返します。throwせずにredirect()/error()を呼び出す →throwします。- レイアウトでエンドポイントを保護する → 各
+server.tsで認証を確認します。 - フックだけで認証ポリシーを実施する → フックで
localsに値を設定し、ルートで適用します。 - DB/セッションの基盤が不足しているときに認証を黙って省略する → フェイルクローズにします。
- 保護されたルートグループ内にログインを配置する → 認証ページは保護されたレイアウトの外に置きます。
- JS 専用のフォームを作る → ネイティブフォームとアクションを使い、プログレッシブエンハンスメントを適用します。
- クライアント側の検証を信頼する → サーバー境界で検証します。
- shadcn-svelte の検証を生のラッパーでレンダリングする → コンポーネント内で
Field.Field、Field.Label、Field.Errorを使います。 - 認証/検証なしでリモート関数を使う → サーバーエンドポイントとして扱います。
- SSR 中にブラウザー API を使う → エフェクト/ブラウザー専用ロジックで制御します。
- アプリのデータフローに svelte5 のコンポーネントに関するアドバイスを使う → SvelteKit のリファレンスを読み込みます。
クイックリファレンス
| 問題の兆候 | SvelteKit での標準的な対応 | リファレンス |
|---|---|---|
| ルートファイルの選択が不明確 | 実行環境と境界に基づいて選びます | file-naming |
| 保護されたページグループ | +layout.server.ts で認証を確認 | auth |
| 保護されたエンドポイント | +server.ts 内で認証を確認 | auth |
| ブラウザーコード内の DB/シークレット | サーバーの load/アクション/エンドポイント | load-functions |
$effect によるクエリ/ページコントロールの更新 | イベントハンドラーまたはフォーム送信 | load-functions |
| フォーム形式の変更 | フォームアクション | form-actions |
| フィールドの検証エラー | fail(400, { errors }) | forms-validation |
redirect() または error() をそのまま呼び出す | throw redirect(...) / throw error(...) | errors-and-redirects |
load からリッチオブジェクトを返す | シリアライズ可能なプレーンデータ | serialization |
| SSR でのクライアント専用 API | エフェクト/ブラウザーガード | ssr-hydration |
| クライアントからのサーバー読み取りの繰り返し | リモート関数が適していれば query() | remote-functions |
| クライアントからの命令的なサーバー変更 | 検証/認証を伴う command() | remote-functions |
| コンポーネント API/runes の問題 | svelte5 を使います | svelte5 |
リファレンス一覧
ファイルの命名 · レイアウトパターン · Load 関数 · フォームアクション · フォームの検証 · 認証 · エラー/リダイレクト · シリアライズ · SSR/ハイドレーション · リモート関数 · Better Auth · Cloudflare