mcpField
S
sveltekit-best-practices

sveltekit-best-practices

github.com/ofershap開発1379 回インストール36 週間前に更新
リポジトリ

sveltekit-best-practices について

sveltekit-best-practicesは、SvelteKitまたはSvelte 5を扱うコーディングエージェント向けの開発スキルです。Svelte 5のリアクティビティと、データ読み込み、フォームアクション、ルーティングに関するSvelteKitの慣習を示し、Svelte 4のパターンを避けます。

主な機能

  1. 01Svelte 5 runesによるリアクティブ状態管理
  2. 02$props()によるコンポーネントプロパティ宣言
  3. 03load関数によるページデータ取得
  4. 04フォームアクションによる送信処理
  5. 05レイアウト読み込みによる共有データ
  6. 06SvelteKitのエラーページとhooksの慣習

活用シーン

  1. 01Svelte 5コンポーネントとリアクティブ処理の作成
  2. 02サーバー側でのページデータ読み込み
  3. 03プログレッシブエンハンスメント対応フォームの構築
  4. 04共有レイアウトデータとリクエストミドルウェアの設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ofershap/sveltekit-best-practices --skill sveltekit-best-practices

インストール後、Claude Code で/sveltekit-best-practicesを入力して読み込まれたか確認します。

詳細

使用する場面

SvelteKit または Svelte 5 のコードを扱うときに、このスキルを使用してください。AI エージェントは Svelte 4 のパターンで学習しているため、stores、リアクティブ宣言、export let を使った古いコードを頻繁に生成します。このスキルでは、Svelte 5 のルーン、load 関数、フォームアクションを徹底します。

重要なルール

1. Svelte 5 のルーンを使う — Svelte 4 の stores やリアクティブ宣言は使わない

誤り(エージェントがよく生成するパターン):

<script>
  import { writable, derived } from 'svelte/store';
  let count = writable(0);
  $: doubled = $count * 2;
  $: if (count > 5) alert('too high');
</script>
<p>{$count}</p>

正しい例:

<script>
  let count = $state(0);
  let doubled = $derived(count * 2);
  $effect(() => {
    if (count > 5) alert('too high');
  });
</script>
<p>{count}</p>

理由: Svelte 5 のルーン($state、$derived、$effect)は、stores と $: 構文に取って代わります。エージェントは Svelte 4 のパターンをデフォルトで使用します。

2. リアクティブな状態には $state を使う — リアクティブな代入を伴う let は使わない

誤り:

<script>
  let count = 0;
  count = count + 1;
</script>

正しい例:

<script>
  let count = $state(0);
  count = count + 1;
</script>

理由: Svelte 5 では、$state を使ってリアクティビティを明示的に有効にします。通常の let はリアクティブではありません。

3. 計算値には $derived を使う — $: のリアクティブ宣言は使わない

誤り:

<script>
  let firstName = $state('John');
  let lastName = $state('Doe');
  $: fullName = `${firstName} ${lastName}`;
</script>

正しい例:

<script>
  let firstName = $state('John');
  let lastName = $state('Doe');
  let fullName = $derived(`${firstName} ${lastName}`);
</script>

理由: $: は Svelte 4 の構文です。Svelte 5 では、派生値に $derived を使います。

4. 副作用には $effect を使う — $: のリアクティブステートメントは使わない

誤り:

<script>
  let count = $state(0);
  $: if (count > 5) console.log('count is high');
</script>

正しい例:

<script>
  let count = $state(0);
  $effect(() => {
    if (count > 5) console.log('count is high');
  });
</script>

理由: $effect は依存関係が変化したときに実行されます。副作用に $: を使う方法は非推奨です。

5. コンポーネントの props には $props() を使う — export let は使わない

誤り:

<script>
  export let title = 'Default';
  export let count;
</script>
<h1>{title}</h1>

正しい例:

<script>
  let { title = 'Default', count } = $props();
</script>
<h1>{title}</h1>

理由: export let は Svelte 4 の構文です。Svelte 5 では $props() を使います。

6. 双方向バインディングを行う props には $bindable() を使う

誤り:

<script>
  let { value } = $props();
</script>
<input bind:value={value} />

正しい例:

<script>
  let { value = $bindable() } = $props();
</script>
<input bind:value={value} />

理由: props はデフォルトで一方向です。$bindable() を使うと、親から bind:value を指定できるようになります。

7. データ取得には load 関数(+page.server.ts)を使う — onMount での fetch は使わない

誤り:

<script>
  import { onMount } from 'svelte';
  let data = $state(null);
  onMount(async () => {
    data = await fetch('/api/users').then(r => r.json());
  });
</script>
{#if data}{data.name}{/if}

正しい例:

// +page.server.ts
import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async ({ fetch }) => ({
  data: await fetch("/api/users").then((r) => r.json()),
});

<script>
  let { data } = $props();
</script>
{#if data}{data.name}{/if}

理由: load は SSR のためにサーバーで実行されるので、読み込み時のちらつきを防ぎ、SvelteKit のルーティングと連携します。

8. データの変更にはフォームアクションを使う — フォーム送信に API ルートは使わない

誤り:

<form on:submit={async (e) => {
  e.preventDefault();
  await fetch('/api/login', { method: 'POST', body: new FormData(e.target) });
  goto('/dashboard');
}}>

正しい例:

// +page.server.ts
import type { Actions } from "./$types";
export const actions = {
  default: async ({ request, cookies }) => {
    const data = await request.formData();
    // validate, authenticate, set cookie
    return { type: "redirect", location: "/dashboard" };
  },
};
<form method="POST" use:enhance>

理由: フォームアクションはプログレッシブエンハンスメントを可能にし、JS なしでも動作するため、クライアント側で fetch の定型コードを書く必要がありません。

9. 共通のレイアウトデータには +layout.server.ts を使う

誤り:


<script>
  let user = $state(null);
  onMount(() => fetchUser().then(u => user = u));
</script>

正しい例:

// +layout.server.ts
export const load = async ({ locals }) => ({
  user: locals.user,
});

理由: レイアウトの load は一度だけ実行され、データはすべての子ページから利用できます。重複した fetch は発生しません。

10. エラーページには +error.svelte を使う

正しい例:


<script>
  let { status, message } = $props();
</script>
<h1>{status}</h1>
<p>{message}</p>

理由: SvelteKit は load やアクションで発生したエラーを +error.svelte で表示します。各ページで try/catch を使う代わりに、これを使用してください。

11. ユニバーサル load(サーバーとクライアント)には +page.ts を使う

サーバーとクライアントの両方でデータが必要な場合(例:$app/stores やブラウザー API から取得する場合)は、ロジックを +page.ts に記述してください。サーバーだけで使用するデータには +page.server.ts を使ってください。

12. ミドルウェア(認証、リダイレクト)には hooks.server.ts を使う

正しい例:

// hooks.server.ts
export const handle = async ({ event, resolve }) => {
  event.locals.user = await getUser(event);
  if (!event.locals.user && event.url.pathname.startsWith("/dashboard")) {
    return redirect(302, "/login");
  }
  return resolve(event);
};

理由: handle はリクエストごとに実行されます。認証、リダイレクト、locals の設定に使用してください。

13. $app/stores は必要最小限にとどめる — load 関数のデータを優先する

load の props を使ってデータを渡してください。$page、$navigating などは、クライアント側のルーティング状態が必要な場合にのみ使用してください。

14. 再利用可能なテンプレートのまとまりには snippet ブロックを使う(Svelte 5)

誤り:

<script>
  export let slots;
</script>
{#if slots.header}<slot name="header" />{/if}

正しい例:

<script>
  let { header = @render(() => {}) } = $props();
</script>
{@render header()}

理由: Svelte 5 の snippet は、slot ベースの構成に取って代わり、@render と snippet props を使います。

パターン

  • ページデータ:+page.server.ts の load はオブジェクトを返し、+page.svelte は $props() で data を受け取ります
  • enhance を使うフォーム:method="POST" と $app/forms の use:enhance
  • ストリーミング:await せずに load から Promise を返し、テンプレートで {#await data.promise} を使います
  • TypeScript:import type { PageServerLoad, PageProps } from './$types' を使います

アンチパターン

  • svelte/store の writable、readable、derived、get を使わないでください
  • 派生値や副作用に $: を使わないでください
  • props に export let を使わないでください
  • SSR に必要なデータを onMount で fetch しないでください
  • フォームの POST 処理だけを目的に +server.ts API ルートを作成しないでください
  • prop が $bindable() でない限り、bind:value を使わないでください

コメント