svelte5-development について
Svelte 5 Development Skillは、Svelte 5とSvelteKitプロジェクトの開発ガイドです。runes、ルーティング、データ読み込み、フォームアクション、APIルート、実装時の注意点を扱います。
主な機能
- 01Svelte 5のruneリファレンス
- 02SvelteKitのファイル規約
- 03データ読み込みとAPIルート
- 04フォームアクションと検証
- 05よくある問題と実装パターン
活用シーン
- 01runesを使ったSvelte 5コンポーネントの作成
- 02SvelteKitのルートとレイアウトの設定
- 03ページ用データの読み込み
- 04検証付きフォームアクションの実装
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ autumnsgrove/ lattice --skill svelte5-developmentインストール後、Claude Code で/svelte5-developmentを入力して読み込まれたか確認します。
詳細
Svelte 5 開発スキル
有効にするタイミング
次の場合にこのスキルを有効にします:
- Svelte 5 コンポーネントを作成するとき
- SvelteKit のルーティングを扱うとき
- Runes($state、$derived、$effect)を実装するとき
- アクションを使ってフォームを構築するとき
- SvelteKit プロジェクトをセットアップするとき
クイックコマンド
npx sv create my-app # Create SvelteKit project
cd my-app && pnpm install
pnpm dev # Start dev server (localhost:5173)
pnpm build # Build for production
Runes クイックリファレンス
| Rune | 目的 | 例 |
|---|---|---|
$state | リアクティブな状態 | let count = $state(0) |
$derived | 計算値 | let doubled = $derived(count * 2) |
$effect | 副作用 | $effect(() => console.log(count)) |
$props | コンポーネントのプロパティ | let { name } = $props() |
$bindable | 双方向バインディング | let { value = $bindable() } = $props() |
リアクティブな状態($state)
<script>
let count = $state(0);
let user = $state({ name: "Alice", age: 30 });
let items = $state(["apple", "banana"]);
</script>
<button onclick={() => count++}>
Clicked {count} times
</button>
<button onclick={() => user.age++}>
{user.name} is {user.age}
</button>
ディープリアクティビティ:オブジェクトや配列は、変更されると自動的に更新されます。
計算値($derived)
<script>
let count = $state(0);
let doubled = $derived(count * 2);
// For complex computations
let summary = $derived.by(() => {
const total = items.length;
const done = items.filter((i) => i.done).length;
return `${done}/${total}`;
});
</script>
副作用($effect)
<script>
let count = $state(0);
$effect(() => {
console.log(`Count is ${count}`);
document.title = `Count: ${count}`;
// Cleanup function
return () => {
console.log("Cleanup");
};
});
</script>
コンポーネントのプロパティ($props)
<script>
let { label, disabled = false, onclick } = $props();
</script>
<button {disabled} {onclick}>{label}</button>
TypeScript のプロパティ
<script lang="ts">
interface Props {
label: string;
disabled?: boolean;
onclick?: () => void;
}
let { label, disabled = false, onclick }: Props = $props();
</script>
SvelteKit のファイル規約
| ファイル | 目的 |
|---|---|
+page.svelte | ページコンポーネント |
+page.server.js | サーバー専用のロード/アクション |
+layout.svelte | 共通レイアウト |
+server.js | API エンドポイント |
+error.svelte | エラーバウンダリー |
データの読み込み
// src/routes/posts/+page.server.js
export async function load({ fetch }) {
const response = await fetch("/api/posts");
return { posts: await response.json() };
}
<script>
let { data } = $props();
</script>
{#each data.posts as post}
<article>
<h2>{post.title}</h2>
</article>
{/each}
フォームアクション
型安全なフォーム検証(Rootwork パターン)
信頼境界で型安全にフォームを処理するには、parseFormData() を使用します:
// src/routes/login/+page.server.ts
import { fail, redirect } from "@sveltejs/kit";
import { parseFormData } from "@autumnsgrove/lattice/server";
import { z } from "zod";
const LoginSchema = z.object({
email: z.string().email("Valid email required"),
password: z.string().min(8, "Password must be at least 8 characters"),
});
export const actions = {
default: async ({ request, cookies }) => {
const formData = await request.formData();
const result = parseFormData(formData, LoginSchema);
if (!result.success) {
const firstError = Object.values(result.errors).flat()[0];
return fail(400, { error: firstError || "Invalid input" });
}
const { email, password } = result.data;
// Authenticate with validated, typed data
const token = await authenticate(email, password);
if (!token) {
return fail(401, { error: "Invalid credentials" });
}
cookies.set("session", token, { path: "/" });
throw redirect(303, "/dashboard");
},
};
メリット:
- 型安全:
result.dataはLoginSchemaとして完全に型付けされています - 構造化されたエラー:
result.errorsはフィールド → メッセージのマップです - 信頼境界で
asキャストが不要です
<script>
import { enhance } from "$app/forms";
let { form } = $props();
</script>
<form method="POST" use:enhance>
<input name="email" value={form?.email ?? ""} />
{#if form?.missing}
<p class="error">Email required</p>
{/if}
<button>Submit</button>
</form>
API ルート
// src/routes/api/posts/+server.js
import { json } from "@sveltejs/kit";
export async function GET({ url }) {
const posts = await getPosts();
return json(posts);
}
export async function POST({ request }) {
const data = await request.json();
const post = await createPost(data);
return json(post, { status: 201 });
}
よくある落とし穴
分割代入でリアクティビティが失われる
// ❌ Bad
let { count } = $state({ count: 0 });
// ✅ Good
let state = $state({ count: 0 });
state.count++;
Each でキーが不足している
{#each items as item}
{#each items as item (item.id)}
プログレッシブエンハンスメントを使用する
<script>
import { enhance } from '$app/forms';
</script>
<form method="POST" use:enhance>
エラーフィードバック(Signpost + Toast)
Toast は、ユーザーアクションに対する主要なクライアント側フィードバックです:
import { toast } from "@autumnsgrove/lattice/ui";
// After successful action
toast.success("Post published!");
// After failed action
toast.error(err instanceof Error ? err.message : "Something went wrong");
// Async operations
toast.promise(apiRequest("/api/export", { method: "POST" }), {
loading: "Exporting...",
success: "Done!",
error: "Export failed.",
});
// Multi-step flows
const id = toast.loading("Saving...");
// ... later
toast.dismiss(id);
toast.success("Saved!");
**Toast を使わないケース:**フォームの検証(fail() とインラインエラーを使用)、ページの読み込み失敗(+error.svelte)、永続的なお知らせ(GroveMessages を使用)。
サーバー側のエラーには Signpost コードを使用します:
import {
API_ERRORS,
buildErrorJson,
throwGroveError,
logGroveError,
} from "@autumnsgrove/lattice/errors";
// API route: return structured error
return json(buildErrorJson(API_ERRORS.RESOURCE_NOT_FOUND), { status: 404 });
// Page load: throw to +error.svelte
throwGroveError(404, SITE_ERRORS.PAGE_NOT_FOUND, "Engine");
型安全なエラー処理(Rootwork ガード)
フォームアクションで捕捉した例外を安全に処理するには、isRedirect() と isHttpError() を使用します:
import { isRedirect, isHttpError } from "@autumnsgrove/lattice/server";
import { fail } from "@sveltejs/kit";
export const actions = {
default: async ({ request }) => {
try {
const formData = await request.formData();
const result = parseFormData(formData, MySchema);
if (!result.success) {
return fail(400, { error: "Validation failed" });
}
// Process data
const response = await processData(result.data);
throw redirect(303, "/success");
} catch (err) {
// Redirects and HTTP errors must be re-thrown
if (isRedirect(err)) throw err;
if (isHttpError(err)) return fail(err.status, { error: err.body.message });
// Unexpected errors
console.error("Unexpected error:", err);
return fail(500, { error: "Something went wrong" });
}
},
};
詳しくは、完全なリファレンスとして AgentUsage/error_handling.md と AgentUsage/rootwork_type_safety.md を参照してください。
プロジェクト構成
src/
├── lib/
│ ├── components/
│ └── server/
├── routes/
│ ├── +layout.svelte
│ ├── +page.svelte
│ └── api/
├── app.html
└── hooks.server.js
Grove 固有:GroveTerm コンポーネント
自然をテーマにした用語を含む Grove UI を構築する場合は、用語をハードコードせず、必ず GroveTerm コンポーネントスイートを使用してください。これにより、ユーザーの Grove Mode 設定(デフォルトでは標準用語、オプトインで Grove 用語を使用)が尊重されます。
<script lang="ts">
import { GroveTerm, GroveSwap, GroveText } from "@autumnsgrove/lattice/ui";
import groveTermManifest from "$lib/data/grove-term-manifest.json";
</script>
<GroveTerm term="bloom" manifest={groveTermManifest} />
<GroveSwap term="arbor" manifest={groveTermManifest} />
<GroveText
content="Your [[bloom|posts]] live in your [[garden|blog]]."
manifest={groveTermManifest}
/>
コンポーネントのソースは libs/engine/src/lib/ui/components/ui/groveterm/ を参照してください。UI デザインの詳細なガイダンスは chameleon-adapt スキルを参照してください。
関連リソース
以下を含む完全なドキュメントは AgentUsage/svelte5_guide.md を参照してください:
- 高度な rune パターン
- 再利用可能なマークアップのためのスニペット
- パフォーマンスの最適化
- TypeScript との統合