最短で学ぶSvelte！チュートリアル要約＆サンプルコード

# はじめに
Svelteに興味があるけれど、公式チュートリアルを一通り読むのは大変…と感じる方もいるかもしれません。
本記事では、Svelteの基本的な概念から応用的な使い方までを、実践的なサンプルコードとともに要約しました。

ショートハンド記法・リアクティブな変数・ストア・非同期処理・イベント管理・CSSバインディングなど、Svelteの主要な機能をできるだけ分かりやすくまとめています。

チュートリアルの補助的な資料として、学習の手助けになればと思っています。
これからSvelteを学ぶ方、すでに触れたことがある方の復習としても活用いただければ幸いです。

※当記事ではSvelte単体の解説記事になります。SvelteKitの内容は含まれてません

# この記事で紹介する内容
| **機能** | **概要** |
|---------|---------|
| **ショートハンド記法** | プロパティを簡潔に記述 |
| **動的HTML (`@html`)** | 文字列として渡されたHTMLをレンダリング |
| **`$derived`** | 既存のデータを元に派生した値を計算 |
| **`$inspect`** | データの変更をリアルタイムで確認 |
| **keyed each** | 配列の要素削除時に連動させる |
| **非同期処理 (`await` ブロック)** | 非同期データの取得と表示 |
| **イベントの親子順 (`capture`)** | 親が先にイベントを処理する |
| **`bind` を活用したデータバインディング** | フォーム入力などの要素とデータを連携 |
| **CSSクラスの適用** | クラスを条件付きで適用する方法 |
| **get / set** | クラスのカウンター管理 |
| **store** | 状態をコンポーネント間で共有 |
| **`@snippet`** | 再利用可能なスニペットを定義 |
| **`svelte:boundary`** | エラーハンドリングの実装 |

# ショートハンド
Svelteでは、プロパティのショートハンド記法が使えます。
通常の記法と比較すると簡潔になります。
[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE32QsU7DQAyGX8X10iVp9jSNhLqwM3IMpbgi0uXulLioKIoE3AITExLiBWBi6UAH3uYEgrdAV7VFKQmLh9_-f9tfhWqSE8bobt6cfXb23dlbZ--dXTp7hwHOMkklxscV8qXxg17AYGs7MGZQXpBkr51OSmrTp1oxKS4xxqScFpnhVCiALDe6YBifZ_JsrHOjFSmGWaFz6A-iprwJ6w-9URKD3wMjEHi0bggcCpVEu3Shkl4YwvfV08dq5a5fv14eP5cPEIapUMneRh81qnytIfr1thL5L2cXgQEyLRhjLuZUBx3oWh9sUuwa6QBKizXQLZ4_REx6SFLqYHNpL4lMKlTz2pP6B2oTOvcTAgAA)
```App.svelte
<script>
  let name = "Svelte";
</script>

<!-- 通常の記法 -->
<Component name={name} />

<!-- ショートハンド記法 -->
<Component {name} />
```
```ChildComponent.svelte
<script>
  export let name;
</script>

<p>Hello, {name}!</p>
```

# 動的HTML
`@html`を使用すると、文字列として渡されたHTMLをレンダリングできます。


:::note warn
ユーザー入力を直接 @html に渡す場合、XSS（クロスサイトスクリプティング）の危険があるため、十分に検証してください。
:::

```App.svelte

<script>
  let htmlContent = "<strong>Dynamic HTML</strong>";
</script>

<p>{@html htmlContent}</p>
```
![スクリーンショット 2025-03-13 13.58.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/d0f7a1e5-30ac-4b36-86fc-0d1fc03cc1d5.png)


# `$derived` を使った派生状態の管理
Svelteの $derived を活用すると、他の変数の状態を元に派生値を計算できます。

[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE4WQQU6DUBCGrzJ5aVJIX0qrrhBIegEvULqgMLUoHQhvaDSExTMu3egZXBgXHqGXeRcxUEAXJm7_-f75_5laUHRA4YpJgmV6xERIsUszVMJd14Ifi3bWCkIO5Koo5uqIGbfaNlL4lx7nxEishCs8FZdpwUFIIWfIQNVhi6UCHyaKI0ZrvZRwIeFSwtXGvh4wzjnKWqgvZvW-eYlJFaNlsQSywQ-AYQYkYWF3ZgrZcUaXeXoz-t3oV6M_jX42-qPba_TLcmH0l9Eno0-953fq_2F91q6imNOcIEqSm85k2VC3k5CHJUWl9mP9DOmW9zCD5fnWJiTP-XkReUVQD-xdnpI1hRlM7QZ8qLt2jecUZ3JbMecEOcVZGt_79Vih6Z69ShKI-kvakDMehCSkYHxg4XJZYbNpvgGPDYv7BQIAAA==)
```App.svelte
<script>
	let numbers = $state([1, 2, 3, 4]);
	let total = $derived(numbers.reduce((t, n) => t + n, 0));

	// $derivedをつけないとtotalが10のまま
	// let total = numbers.reduce((t, n) => t + n, 0);

	function addNumber() {
		numbers.push(numbers.length + 1);
	}
</script>

<p>{numbers.join(' + ')} = {total}</p>

<button onclick={addNumber}>
	Add a number
</button>
```

# `$inspect`で状態が変化する度にログを出す
開発時に変数の変化を監視するために `$inspect`を使用できます。
また、本番環境等ではログが出ないようになります。

[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE21QzUrDQBB-lWXpIaWlaa9pGvA5jNB22UownYRmKkoINJuDVeuloJeiiJYKFnrRkyB9mDHqY8g2bU_eZr6f-T4m5tDpS27xkgdRKAXyKu95voy4dRhzPA81pwFe3SkPwrAWnUofNdbtRPI_XASAEjDiFrcjMfBCdFxgzJfIRDAEZC1WirCD0qiXmy5ozjRZe9eizUhNvz7XlD5TuthaKF3l88vv2TupKWWvpJ4oW5Jak5pT9kjpy8_s-Xdxp2_t7hgbZ5HgommyfHyfzx4oXdFoUqfRjZ7SdX7xkV_NKL0lNSF1rbUigCjwZc0Pjg2XbzeXV4su5aJybwgCvQCYB2Ig-xLQKLNYM2zbudJijaYGEhdsc_8JF-zQIbUktaDsjbKxxeKNIbHN0HHB7g4RA2ABCN8TJ614H5A4lYZtFrTDqxzlGXILB0OZHCV_QJ-g6M4BAAA=)

```App.svelte
<script>
  let count = $state(0);

  // `$inspect` を使って count の変更をリアルタイムで監視
  $inspect(count);

	// 初回の「0」のみ出力される
	console.log("console", count)

  function increment() {
    count += 1;
  }
</script>

<p>カウント: {count}</p>
<button onclick={increment}>+1</button>
```

# keyed each で削除時に連動させる
リストをレンダリングする際に、各要素に一意のキーを持たせることで削除時の動作を適切に制御できます。
[該当チュートリアル](https://svelte.jp/tutorial/svelte/keyed-each-blocks)
```App.svelte
<script>
	import Thing from './Thing.svelte';

	let things = $state([
		{ id: 1, name: 'apple' },
		{ id: 2, name: 'banana' },
		{ id: 3, name: 'carrot' },
		{ id: 4, name: 'doughnut' },
		{ id: 5, name: 'egg' }
	]);
</script>

<button onclick={() => things.shift()}>
	Remove first thing
</button>

<!-- {#each things as thing} にするとname の値が更新されるが、絵文字は更新されない -->
{#each things as thing (thing.id)}
	<Thing name={thing.name} />
{/each}
```

```Thing.svelte
<script>
const emojis = {
    apple: '🍎',
    banana: '🍌',
    carrot: '🥕',
    doughnut: '🍩',
    egg: '🥚'
};

let { name } = $props();

const emoji = emojis[name];
</script>

<p>{emoji} = {name}</p>
```

# 非同期処理 (await ブロック)

await ブロックを使うと、非同期処理の状態に応じてUIを更新できます。
[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE22OQU-EQAyF_0pTL5AQIB4RSUw08eDBgzfxMA5FJoEOmSmLmwn_3cyyGA_e2ve-99qArCbCCp9pHC2s1o0dJNQZoS7FDHszksfqPaCc58hFAbMj9TDPuT_RKFH7VJ7-07VlIRaPFdZeOzNL0zKA8mfW0C-sxViGnkQPj0pUkkKIPoAjWRwD0wqvzk7GU-LI2_FEcN8cEIAneTMT2UWSJI3OFUpafLGqow5ibYtpBrdlWaZ3e3Dbh63luvh9q-Vwo1Zl5O8_W-TquYlthr_yPK-LuWk5VDIQQ6dEHUi4LIetlegByDnrDuApLhWEXbyCxeXkhhkKfQtW4hbaPrYf4cLVip0BAAA=)
```App.svelte
<script>
  async function fetchData() {
    return new Promise(resolve => {
      setTimeout(() => resolve("Loaded Data"), 2000);
    });
  }
</script>

<!-- 2秒後に"Loaded Data"と表示される -->
{#await fetchData()}
  <p>Loading...</p>
{:then data}
  <p>{data}</p>
{:catch error}
  <p>Error: {error}</p>
{/await}
```

# capture でイベントの親子の順番を逆にする
通常、イベントは子要素から親要素へ伝播しますが、`capture`を使うと親要素が先にイベントを処理できます。
[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE3WPMU7DQBBFr7JMZUsW7o2xhNJQ0mOKze5EWbHMWuuxAVkuTJGGji4dHQUSB-A4uIi4BVoHlAKnG_15_8-fDkjeIWRwidY6ce-81SJCbRh1DAmsjMUasusO-LEKXBAg-XNdVNVp3aLloC1ljXO6csRIXEMGea28qbgoqeQ0FVfSI7H42ryIxdpYPQ4f36-bcXjbbT93w_s4bMen55KEWDWk2DgSa0na4t63sEbdRrHoAiGEtOg5KuE3VIUt6pMS4rMA9DM509EjMdNuLiVPD09Qrk0rHE2YkhU3Hs-7fyX7Inj3QpjyZcPs6Ijx0KovpjlP93wRjmvTFpAA4wNDxr7B_qb_AelMYHPGAQAA)
```App.svelte
<script>
	// Parent → Childの順で発火する
  function handleParentClick() {
    alert("Parent clicked!");
  }
  function handleChildClick() {
    alert("Child clicked!");
  }
</script>

<div onclickcapture={handleParentClick}>
  Parent
  <button onclickcapture={handleChildClick}>Child</button>
</div>
```

# bind を使用したデータバインディング
Svelteでは bind: を使うことで、フォームの入力値や要素のプロパティをリアクティブに連携できます。
[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE22Pu0oDQRSGX-V46l3Tj7MDgq34ABmLTRxhYT0O2bNBWRacYCw0YiUoNhYKYsDe5zmMzxHGS0BI-_03_g6pPHGocFTREWZ4XNWuQTXskM994glg9ufa9X67mbqaExuVjdvEx6fEjrhBhboZTyrPxhJA7RiSHQqwaHHHkh6sZUt6K89BV-RbhrRdWGR3xhZhWtatK7qU7Q1IWEp4lNmNhLc4f43XT_FqHj8-JSz2DvYlLOPd7dfDs4R7mS0kvEu4hDw3ljZ2p9vq34Al7c1v8cWLgh-sB95g9p1CxZPW9Yf9CsYZyXs8AQAA)
```App.svelte
<script>
  let name = "";
</script>

<!-- <input type="text" value={name}> にすると入力内容がDOMに反映されない -->
<input type="text" bind:value={name}>
<p>入力値: {name}</p>
```

# CSSのクラスの付け方
Svelteでは class に配列を渡し、オブジェクト記法を用いることで条件付きクラスを適用できます。

[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE3WRwU7DMAyGX8X4BFJXNpA4ZOukiVfgtu6Qpl6JFpIqdsdG1XdHbdeBNLglv_8vv-206PUHoULDjAnurSNGtW1RznUv9wImk2lT1ykfyUmvFZrpL90EL-SFUeGKTbS1rHMP4EjA8saIPRJksNeOaZn71ePVk_tV0YgED8Er46w5ZO39A2Tr39zddO7AOM2ctdscRyzHBFrI0fJMD5Yc1Q_a7bqhj7dQVY7gtYf7-JEd41nOjgZXeumk7S8AtS5L6ysFi3l9gqd5fVqOhX3wMmP7RQoWL1e1CLGkqMAHTxfJNJFDVFAH64XiZNTmUMXQ-HJmguvrVdTnCRmVz3cr0ysStWcrNnh1w8I8febB1_XDAKTXRUxj3MYVrqH_4rrhd4aVYIJCJ0ElsaFu130DsqohbTcCAAA=)

```App.svelte
<script>
  let isActive = false;
</script>

<button on:click={() => isActive = !isActive} class={["button", { "is-active": isActive }]}>
  Toggle Class
</button>

<style>
  .button {
    padding: 10px 20px;
    font-size: 16px;
    border: none;
    cursor: pointer;
    background-color: gray;
    color: white;
    transition: background-color 0.3s;
  }

  .is-active {
    background-color: blue;
    color: white;
  }
</style>
```

# get / set
Svelteでは、クラスの プライベート変数 (#) を利用しつつ、get / set を活用することで、カウンターの状態管理を安全に行う ことができます。
[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE5WSwWrbQBCGX2XY9iATx3KuiiQIfYdcqkIUdZIuVVZid-S2CEFXKcU-lEIL7SG9FEICySGHlh6C8css9tWvEBRV7tqklF4Wdvafb_752ZKJ-ASZx46RwAWFxPrsiKeomPe0ZPQmbx6bAut30r08H6gRptTUDmOFD9WTTBAKUsxjvkokzymMBECSxkrBk6wQhBLKpgTwKGnuEMBjRTGhM-ztRqJ9SjKhSBYJZdLhghOP0_04LRACGPa6fgB6wdVghbGFu62k6oCuCwfHSAdg9KWpr019YU5_mNOxqT_NP36Zz762siaOe5xjTZFIhRT2sIfo6i_088ni7OdyOh4uvl0tbt8afW30jdFXpp40p35n6rHR7-fjX_PZ9-V00hLVysioWcdyw4-grUG4HsZmHCMrB4AKMFVoq-MUJTkRWzOsbzZ8Xhr9weiZ0Wem_ryc6oj1_jA3YuAikXiC6-ndm-o82Q63YGczx-f4P4BtG7CipF1yKCEAga-6b-c0xn139Ssj4eehvbwH5e_Gdlzlu3mrOyyIMgGZSFKevAxKpwdB2E0ZWGtX4daO77by8J-N1rpVuG03sj4jfE3MI1lg9ay6A8XFUmiwAwAA)

```App.svelte
<script>
  class Counter {
    #count = $state(0);

    constructor(initialValue = 0) {
      this.#count = initialValue;
    }

    // `get` でカウントを取得
    get count() {
      return this.#count;
    }

    // `set` でカウントを変更（0未満にはならないよう制御）
    set count(value) {
      if (value >= 0) {
        this.#count = value;
      } else {
        alert("カウントは0未満にはできません！");
      }
    }

    increment() {
      this.count = this.#count + 1;
    }

    decrement() {
      this.count = this.#count - 1;
    }
  }

  let counter = new Counter();
</script>

<p>カウント: {counter.count}</p>

<button onclick={() => counter.increment()}>+1</button>
<button onclick={() => counter.decrement()}>-1</button>
```

# store
Svelteの store を活用すると、コンポーネント間での状態管理 が簡単にできます。
ここでは writable を使ってカウンターを作成し、リアクティブな更新されるようにします。
[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE42QzWrbQBSFX-V2CMQmViRvFVmlz1F3YStjEJWvhHTVH4TAGpci000JpF3YpdSYLhIIhZbSFid5mFsJusorlIlaNQsvuhmGe74z587JBI6mUtgioTCWoicmfiATYT_MBD2PtKAHovcXexBFh8kTGZCejUeJ3DX3QiSJlAhbOIkX-xG5QwTwp1EYE2TwNPZpNA4k5DCJwynsN07zdof9oyFq2jSB1Tmrjzz_zPOSiwtW3_VFrVmd_Lxc1eXrm21Zle_q1ftqtgHrZrvQxkASeGGKBIM2qWN1dz2rrnm-ZXVSrd_W35ZcnHKxZPXq15t1ffpJ05MUPfJDBB-9WE4lUqcLmVagyThMo-MRyQ7CwAWEA-jrIIC8SWv9x_I__B0EFyy4DwgG9MEGq9s90htb9eqs_jHj4pyLM1YLfRYvWJVcvKzKr9XVhz-Zjtn2PUQncu82aEO2d5uZO2bUAPcMA_61WlxUs41uY7Ool1_AMNwhOuOUKEQI0Qt87_Ega4vI3YO-YzbyLrD9ce4ad0HREySfkbApTmX-KP8N-dwlbIICAAA=)

```App.svelte
<script>
  import { writable } from 'svelte/store';

  // カウントのストアを作成（初期値 0）
  let count = writable(0);

  // カウンターを増減させる関数
  function increment() {
    count.update(n => n + 1);
  }

  function decrement() {
    count.update(n => (n > 0 ? n - 1 : 0)); // 0未満にならないよう制御
  }
</script>

<p>カウント: {$count}</p>

<!-- ストアの値を変更 -->
<button onclick={increment}>+1</button>
<button onclick={decrement}>-1</button>
```

# `@snippet`
Svelteの @snippet は、コンポーネントの一部を再利用しやすくするための機能です。

## 基本の`@snippet`の使い方
[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE22PMW7CQBBFrzIMDZG8ce9sLOjoKVkKByaWpWVY7U4QaOWCC3AFGmoOkOtEUa6B1nYiRaKZ4s1_o_kRudoSFhi4cY7k63T7vnz-XM_9xAzfG0sBi2VEObqUTACzX2_m3HPYk5XE3qpAj_h6x0IsAQvUYe0bJ6VhAEsCWwqhqgleweCcrN1lsOi0kcEXwzr_yxvWI6XgfwiUKg3HqSfekIfaE0nD9aJvM3lqkxfHQ7vHe9GujMMfrc5dOpgPRucbxgyFDoKF-A9qV-0dpNcPQTcBAAA=)

```App.svelte
<script>
  let message = "Hello, Svelte!";
</script>

<!-- Hello, Svelte! -->
{@render greetingSnippet()}

{#snippet greetingSnippet()}
	<p>{message}</p>
{/snippet}
```

## `@snippet`にプロパティを渡す
特定の text を表示するボタンを @snippet にし、異なるボタンを作成できます。

[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE32QwWoUQRRFf-XluZmGHmff6Wk0LuLGL7BczHS_xMaeV01XjTE0DVaXSMSFYAiCuNCNiyhGyCog-DHvA_yFUNOjE8VkV9x769S91SLPFoQJ3qeq0nCgm6qAERWlpSLCGPfKigwmD1u0h3XIBQHj37fu1vVt85QqG7T5zND_9FyzJbYGE0y3xmPYmDAeZ4pTkzdlbTPFABVZWJAxs32CKSgU_0H6n-LPpX8r_Zn4U_E-HNw7cZ_FvRH3UdyJuBe_fjiF24oDZW_JuS01Q_54xvv0YACOmA7WxwjakIMrb23M7WB1itPJn2KK0zpr1-EundSDFtZIfyH-tfj3oZg_EvdN_NdQ2D9ftT2X_vuws73TEBfUQL40Vi92ltZqHqnh6xXGMIpgmv3TWaG4Y-mPxX0R90nc2WpnFHXX83a1LuaHdBPxRPojcS_FnUr_akNU3N4yXNY12b-hlp7ZGDTfq8r8SUgCpPOVBZqTPKjTdm13WRviXToZEmH5ZE3tFGOMwcbENkvqHnWXvwPaSoICAAA=)

```App.svelte
<script>
  let message = "ボタンをクリックしてください！";

  function changeMessage(newMessage) {
    message = newMessage;
  }
</script>

<p>{message}</p>

<!-- スニペットのレンダリング -->
{@render customButton("Hello", () => changeMessage("こんにちは！"))}
{@render customButton("Goodbye", () => changeMessage("さようなら！"))}

{#snippet customButton(text, onClick)}
  <button onclick={onClick}>{text}</button>
{/snippet}
```

# `@snippet`を使ったコンポーネントの分割

Svelteの`@snippet`を使って、コンポーネント間で動的なスニペットを受け渡し、状態を共有できます。
[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE5VTy4rUQBT9lUvpogOZbt2mk0adjRtXLo2LTFLdE6xUFVXVjyEErLTIyAwIMwwiuNCFIuNiFoIrwY-5OFt_QfLottNjg-4u93HOvadO5YRHGSUeeUgZEzAXiiXQo0lqaOIQl4xTRjXxnuTEHMmqr0oQdzV1X8q-nlFmqtxBpOnf8rHghnKjiUd8HatUmlHIAdJMCmVg_zBliaIcxkpkEJL-YJVpEUIyDHk1wKiBjGodTSgEEBJcvsPyBy6_YnmG5RUuL3G5rAL7Bu0ntK_Rvkd7gfbFr-92jTKe8tikgkN8GPEJfdQA9jidt6EDedUHG1x_isOqVITcH6wvCbm_vqEdCfI2KEDw_U2eIO_QFrUS-T1FeUIVxFNtRPZgaozgvRbC7S7q1OQrwpo-v6V5KiU1_zoP4MvRekV_IOst_IN6DgSPWRo_C_KeA8FoS6aQoD3H8hztF7Qf0F7hc_u4fqZaY6cYXZ9-vj75hvYtlif-oMH8P_wLLI_RvkR7ieWrG_g_jz9iedoFzwetAkXIiUsMXRjiGTWlhbvDuFse67r3ZnGHhStH5hC37S6sJd96dSgggNtSCal7znDbPUk6g5hFWgchaU7am6tISqpCsuWPlqm3i6hxR5LOGmRtjhitIfpd4JXFk1RLFh15MGZ0MWxyVbiXpIrW38SDWLBpxtviJJIe3L0jFxsfoSXpCv-0-A19PqYGWwQAAA==)
```App.svelte

<script>
  import Children from "./Children.svelte";

  let message = "ボタンをクリックしてください！";

  function changeMessage(newMessage) {
    message = newMessage;
  }
</script>

<Children message={message} onChangeMessage={changeMessage}>
  {@render customButton(message, changeMessage)}
</Children>

{#snippet customButton(message, changeMessage)}
  <p>{message}</p>
  <button onclick={() => changeMessage("こんにちは、Svelte！")}>挨拶する</button>
  <button onclick={() => changeMessage("さようなら、Svelte！")}>別れる</button>
{/snippet}
```

```Children.svelte
<script>
  let { children, message, onChangeMessage } = $props();
</script>

<div class="button-wrapper">
  {@render children(message, onChangeMessage)}
</div>

<style>
  .button-wrapper {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
</style>
```


# `svelte:boundary`を使ったエラーハンドリング
[Playgroundはこちら](https://svelte.dev/playground/hello-world?version=5.23.0#H4sIAAAAAAAAE31SPWvcQBD9K5NNwHcgJNeyJAiBtIFUCVEKnW4uCEuzi3Zkn1i2uFOVD0iTxlVaJ1UgXfC_Edh_w2ils32JL9UO7828x8xbIyirUISi31723Y--u-q7r333u-8-9t3Podj--g8lPLEqStQifGcEt2pQGgDh7XSfK-XrMyx5wBaZxsfwXBIjsRahiHReF4qTlFIuKiVrhpdldtq-kJWShMSwqmUFR36wD09iRycpRcG9BkUjHi5kQ8usbkES1rWsYzPDOcQJ5JK0LNF36Azn1llHf5kGTixl81RToRQyrLKixOXMjXlQo0ae26El5Uglr6TST8A41q9Q6-wD2ihQydSxaJglgaS8LPLT2Lh5m7wenigYWddrgsnQusX2l0lSEp5gXLMIuW7QegdSePRW-4EcajmQTYkMlWw0QgzPNGeMMwPrEI49aEM4Bjs_lMR5QUt5PmhIcgqVPMO7OMx4IEf4a4gB_bwskPjNyUOmfcC8dYy1KY0pRSox07xdT1W7Oz79c_qZ893tQk1Zjl_g-tNVv7noN5f3_3_z5ebiz8237wO-_Tzsd5fUfg7v7S17_ciGWQMAAA==)



```App.svelte
<script>
	import FlakyComponent from './FlakyComponent.svelte';
</script>

<svelte:boundary onerror={(e) => console.error(e)}>
	<FlakyComponent />

	{#snippet failed(error, reset)}
		<p>Oops! {error.message}</p>
		<button onclick={reset}>Reset</button>
	{/snippet}
</svelte:boundary>
```

```FlakyComponent.svelte
<script>
	let mouse = $state({ x: 0, y: 0 });
</script>

<svelte:window
	onmousemove={(e) => {
		mouse.x = e.clientX;
		mouse.y = e.clientY;
	}}
/>

<p>{mouse.x}x{mouse.y}</p>

<button onclick={() => mouse = null}>
	押すとエラーが発生する
</button>

```

処理は下記の通りです

- ボタンを押すと mouse = null になり、エラーが発生 → `<svelte:boundary>` が処理される
- `FlakyComponent.svelte`でエラーが発生すると、onerror でログを出力し、failed snippet を表示
- `reset()`を押すとエラーがリセットされる

![スクリーンショット 2025-03-13 17.45.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/07a1ade7-e206-4b85-8e49-57350f05d674.png)

## おわりに
本記事では、Svelteの公式チュートリアルを要約しながら、基本の主要な機能を解説しました。

| **機能** | **概要** |
|---------|---------|
| **ショートハンド記法** | プロパティを簡潔に記述 |
| **動的HTML (`@html`)** | 文字列として渡されたHTMLをレンダリング |
| **`$derived`** | 既存のデータを元に派生した値を計算 |
| **`$inspect`** | データの変更をリアルタイムで確認 |
| **keyed each** | 配列の要素削除時に連動させる |
| **非同期処理 (`await` ブロック)** | 非同期データの取得と表示 |
| **イベントの親子順 (`capture`)** | 親が先にイベントを処理する |
| **`bind` を活用したデータバインディング** | フォーム入力などの要素とデータを連携 |
| **CSSクラスの適用** | クラスを条件付きで適用する方法 |
| **get / set** | クラスのカウンター管理 |
| **store** | 状態をコンポーネント間で共有 |
| **`@snippet`** | 再利用可能なスニペットを定義 |
| **`svelte:boundary`** | エラーハンドリングの実装 |


本記事がSvelteの学習や復習の助けになれば幸いです！  
最後までお読みいただき、ありがとうございました！