mcpField
S
svelte-template-syntax

svelte-template-syntax

github.com/full-stack-skills開発4137 回インストール5 週間前に更新
リポジトリ

svelte-template-syntax について

Svelte 5 のテンプレート構文を扱う開発者向けのリファレンススキルです。マークアップ、ブロック、バインディング、イベント、スニペット、アニメーションを解説し、例とトラブルシューティングも掲載しています。

主な機能

  1. 01条件・リスト・非同期ブロックを解説
  2. 02スニペットの定義と描画を説明
  3. 03バインディングとイベント構文を参照
  4. 04アタッチメント、アクション、ディレクティブを解説
  5. 05トランジションとリストアニメーションを解説

活用シーン

  1. 01条件レンダリングやリスト表示を実装する
  2. 02スニペットを再利用しコンポーネント内容を渡す
  3. 03フォーム要素にバインディングとイベントを追加する
  4. 04要素にトランジションや並べ替えアニメーションを付ける

インストール

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

$npx skills add https://github.com/full-stack-skills/svelte-skills --skill svelte-template-syntax

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

agents/
openai.yaml
examples/
animations.md
attachments.md
await-expressions.md
basic-markup.md
bind-directives.md
event-handlers.md
if-await-snippet.md
README.md
render-tags.md
snippet-advanced.md
style-class.md
transitions.md
use-action.md
references/
animations.md
attachments.md
await-expressions.md
basic-markup.md
bind-reference.md
event-reference.md
if-await-each.md
README.md
snippet-advanced.md
snippet-render.md
special-tags.md
style-class.md
transitions.md

詳細

Svelte テンプレート構文リファレンス(Svelte 5)

このスキルでは、基本的なマークアップ、ブロック式、テンプレートタグ、イベント処理、属性バインディング、style/class ディレクティブ、アタッチメント/アクション、トランジション、アニメーションなど、Svelte 5 のテンプレート構文を扱います。

このスキルを使用する場面

ユーザーが Svelte コンポーネントのテンプレート構文を記述または理解したい場合に使用します。条件付きレンダリング、リストレンダリング、非同期処理、スニペットの再利用、イベントバインディング、双方向データバインディング、トランジションアニメーションなどが該当します。

重要:基本的なマークアップ

要素タグ
  • 小文字のタグ(<div>)=HTML 要素
  • 大文字またはドットを含むタグ(<Widget>、<my.stuff>)=コンポーネント
属性バインディング

<button disabled={!clickable}>...</button>
<input required={false} />


<button {disabled}>...</button>


<Widget {...props} a="b" />


<div data-active={isActive}>  
イベント処理

<button onclick={() => count++}>+</button>
<input {onkeydown} />          
<button {...handlerProps}>     



テキスト式
<p>{expression}</p>           
<p>Hello {name}!</p>


{@html rawContent}


<p>使用 &lbrace; 和 &rbrace;</p>

重要:{#if ...}

条件付きレンダリング:

{#if count > 10}
  <p>big</p>
{:else if count > 5}
  <p>medium</p>
{:else}
  <p>small</p>
{/if}

{:else if} は複数回連結でき、{:else} が最後のフォールバックになります。

重要:{#each ...}

リストレンダリング:

{#each items as item}
  <li>{item.name}</li>
{/each}


{#each items as item, i}
  <li>{i + 1}: {item.name}</li>
{/each}


{#each todos as todo}
  <p>{todo.text}</p>
{:else}
  <p>No tasks!</p>
{/each}
key を使った効率的な更新
{#each items as item (item.id)}
  <li>{item.name}</li>
{/each}

Key は一意の識別子(文字列/数値)である必要があります。Svelte が効率的に差分を検出して DOM を更新するために使用されます(全体を再レンダリングするのではなく、挿入/移動/削除します)。

分割代入と rest
{#each items as { id, name, ...rest }}
  <li><span>{id}</span><MyComponent {...rest} /></li>
{/each}

{#each items as [id, ...values]}
  <li><span>{id}</span></li>
{/each}
N 回レンダリングする
{#each { length: 8 }, rank}
  <div class:black={(rank) % 2 === 1}></div>
{/each}

重要:{#key ...}

式が変化したときに内容を破棄して再構築します(トランジションアニメーションを実行します):

{#key value}
  <div transition:fade>{value}</div>
{/key}

重要:{#await ...}

Promise の非同期状態に応じた分岐:

{#await promise}
  <p>loading...</p>
{:then value}
  <p>result: {value}</p>
{:catch error}
  <p>error: {error.message}</p>
{/await}


{#await promise then value}
  <p>{value}</p>
{/await}


{#await promise catch error}
  <p>{error.message}</p>
{/await}
コンポーネントの遅延読み込み
{#await import('./Heavy.svelte') then { default: Component }}
  <Component />
{/await}
await 式(Svelte 5.36+ experimental)

<svelte:boundary>
  <p>{await fetchData()}</p>
  {#snippet pending()}<p>loading</p>{/snippet}
</svelte:boundary>

特徴:同期更新、並行実行(独立した await)、<svelte:boundary> の pending スニペット、fork() API。

重要:{#snippet ...}

再利用可能なマークアップスニペットです。Svelte 4 の Slots に代わるものです:

{#snippet card(item)}
  <div class="card">
    <h3>{item.title}</h3>
    <p>{item.body}</p>
  </div>
{/snippet}

{@render card(item)}
スニペットのスコープ

スニペットは外部変数(script 変数または {#each} ブロック内の変数)を参照でき、同じ字句スコープ内の兄弟ノードや子ノードから参照できます:

{#each items as item}
  {#snippet itemCard()}
    <div>{item.name}</div>  
  {/snippet}
  {@render itemCard()}
{/each}
明示的な prop と暗黙的な prop

<Table {data}>
  {#snippet header()}
    <th>Name</th>
  {/snippet}
</Table>


<Table {data} header={myHeader} />
暗黙的な children

コンポーネントタグ内のスニペット以外のコンテンツは、自動的に children スニペットになります:

<Button>click me</Button>


<script>
  let { children } = $props();
</script>
<button>{@render children()}</button>
オプションのスニペット
{@render children?.()}              
{#if children}{:else}fallback{/if}  
スニペットの型定義
<script lang="ts">
  import type { Snippet } from 'svelte';
  let { row }: { row: Snippet<[Item]> } = $props();
</script>
スニペットのエクスポート(5.5+)
<script module>
  export { mySnippet };
</script>
{#snippet mySnippet()}
  <div>content</div>
{/snippet}
プログラムによる作成(createRawSnippet)
import { createRawSnippet } from 'svelte';
const greet = createRawSnippet<[string]>((name) => ({
  render: () => `<p>Hello, ${name}!</p>`
}));

重要:{@render ...}

スニペットをレンダリングします:

{@render snippetName(args)}
{@render children?.()}  
{@render (cond ? a : b)()}  

重要:{@html ...}

生の HTML を挿入します(XSS のリスクに注意):

{@html rawHtml}
  • {expression} は HTML エンティティを自動的にエスケープします
  • {@html expression} は生の HTML を直接挿入するため、内容が信頼できることを確認してください
  • 完全に独立した HTML である必要があります({@html} をまたいでタグをつなげることはできません)
  • スコープ付きスタイルは適用されません。:global で囲んでください

重要:{@attach ...}

要素のマウント時に実行される関数です(Svelte 5.29+)。use: アクションに代わるものです:

<script>
  /** @type {import('svelte/attachments').Attachment} */
  function tooltip(node) {
    const tip = createTip(node);
    return { destroy() { tip.destroy(); } };
  }
</script>

<button {@attach tooltip()}>hover</button>
引数付き Attachment Factory
function tooltip(content) {
  return (node) => {
    const tip = createTip(node, { content });
    return { destroy() { tip.destroy(); } };
  };
}

<button {@attach tooltip(content)}>hover</button>
条件付き Attachment
<div {@attach enabled && myAttachment}>...</div>
インライン Attachment
<canvas {@attach (canvas) => {
  const ctx = canvas.getContext('2d');
  $effect(() => { ctx.fillStyle = color; /* ... */ });
}}></canvas>
再実行の動作

{@attach foo(bar)} は、foo または bar が変化すると完全に再構築されます(アクションとは異なります)。

Action からの移行
import { fromAction } from 'svelte/attachments';
const attach = fromAction(myAction);

重要:bind: ディレクティブ

子から親へデータを反映します:


<input bind:value />
<input bind:value={value} />
Function Bindings(5.9+)
<input bind:value={
  () => value,
  (v) => value = v.toLowerCase()
} />
bind: ディレクティブ一覧
ディレクティブ要素型
bind:valueinput/textarea/select双方向
bind:checkedinput[type=checkbox]双方向
bind:indeterminateinput[type=checkbox]双方向
bind:groupradio/checkbox グループ双方向
bind:filesinput[type=file]双方向
bind:opendetails双方向
bind:valueselect multiple配列
bind:currentTime/paused/volume/muted/playbackRateaudio/video双方向
bind:duration/buffered/seeking/ended/readyState/playedaudio/video読み取り専用
bind:videoWidth/Heightvideo読み取り専用
bind:naturalWidth/Heightimg読み取り専用
bind:innerHTML/innerText/textContentcontenteditable双方向
bind:clientWidth/Height/offsetWidth/Height/contentRectブロック要素読み取り専用
bind:this要素/コンポーネント参照
bind:innerWidth/Heightsvelte:window双方向
bind:scrollX/Ysvelte:window双方向
bind:onlinesvelte:window双方向
bind:fullscreenElementsvelte:document双方向
bind:visibilityStatesvelte:document双方向
$bindable Props

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


<Child bind:value={text} />

重要:use: Action(Svelte 5.29 より前は主流。5.29+ では {@attach} を推奨)

<script>
  /** @type {import('svelte/action').Action<HTMLElement, {color: string}>} */
  function paint(node, data) {
    node.style.color = data.color;
  }
</script>

<div use: paint={{ color: 'red' }}>red text</div>

Action は一度だけ呼び出されます(SSR では実行されません)。引数が変化しても再実行されません。

重要:transition: / in: / out:

要素が DOM に追加/削除されるときのトランジション:

<script>
  import { fade, fly } from 'svelte/transition';
  let visible = $state(false);
</script>

{#if visible}
  <div transition:fade>fades in and out</div>
  <div in:fly={{ y: 200 }} out:fade>flies in, fades out</div>
{/if}
  • transition: は双方向です(途中で反転できます)
  • in: / out: は一方向です(並行して実行され、中断されません)
  • デフォルトはローカルです(|global 修飾子を付けると祖先ブロックに反応します)
組み込みトランジション

svelte/transition に含まれる fade / blur / fly / slide / scale / draw / crossfade

カスタムトランジション
function whoosh(node, params) {
  return {
    duration: 400,
    easing: elasticOut,
    css: (t, u) => `transform: scale(${t})`
  };
}
トランジションイベント

onintrostart / onintroend / onoutrostart / onoutroend

重要:animate:

keyed each ブロック内の既存要素が並べ替えられたときのアニメーション:

<script>
  import { flip } from 'svelte/animate';
</script>

{#each list as item, i (item)}
  <li animate:flip={{ duration: 300 }}>{item}</li>
{/each}
  • 追加/削除時には実行されません(transition: を使用してください)
  • keyed {#each} の直接の子要素である必要があります
  • flip は組み込みです(First-Last-In-First-Out による滑らかな移動)

重要:style: / class: / class

style: ディレクティブ
<div style:color="red" style:width="12rem" style:--columns={n}>
  
</div>

style: ディレクティブは style 属性より優先されます(!important が付いていても同じです)。

class 属性(5.16+ ではオブジェクト/配列に対応)

<div class={{ cool, lame: !cool }}>...</div>


<div class={[faded && 'saturate-0', large && 'scale-200']}>...</div>


<div class={['btn', { primary: true }, ['rounded', { large }]]}>...</div>
ClassValue 型
<script lang="ts">
  import type { ClassValue } from 'svelte/elements';
  const props: { class: ClassValue } = $props();
</script>
class: ディレクティブ(従来の方式。5.16+ では非推奨)
<div class:cool={cool} class:lame={!cool}>
  
</div>

Critical: {@const ...}

⚠️ レガシー構文です。{const x = $derived(y)} に置き換えることをおすすめします。

ブロックレベルのスコープで定数を定義します。

{#each boxes as box}
  {@const area = box.width * box.height}
  <p>{box.width} × {box.height} = {area}</p>
{/each}

{#if}、{#each}、{#snippet}、<Component />、<svelte:boundary> の直接の子要素にのみ使用できます。

Critical: {let/const ...}(5.56+ 推奨)

{#each boxes as box}
  {const area = box.width * box.height}
  <p>{area}</p>
{/each}

コンポーネント内の任意の場所で使用できます。外部の値を参照し、スコープは字句的な兄弟要素/子要素です。

リアクティブです。

{let name = $state(user.name)}
{const greeting = $derived(`Hello ${name}`)}

Critical: {@debug ...}

デバッグタグです。変数が変化すると、コンソールに出力します。

{@debug user, count}       
{@debug}                   

注意:{@debug} が受け取れるのは変数名のみです(式は指定できません)。

Quick Fixes

問題解決策
リストが更新されないキー付きの {#each items as item (item.id)} を使用します
空のリストにフォールバックがない{:else} 分岐を追加します
トランジションが実行されない{#key value} で囲んで再作成をトリガーします
{@html} のスタイルが適用されない:global を使用するか、{@html} を使用しないようにします
Snippet が表示されない同じ字句スコープ内にあることを確認します
await の同期動作が機能しないexperimental.async を有効にします
逆方向のトランジションが機能しないin: / out: ではなく transition: を使用します
animate: が実行されないkeyed {#each} と直接の子要素が必要です

Gotchas

  1. {#each} の key は一意でなければなりません — インデックス(i)を key に使用すると、効率的な更新が正しく行われません
  2. {@html} はスコープ付きスタイルの影響を受けません — 挿入された HTML には Svelte のスコープハッシュが含まれません
  3. {@debug} では式をデバッグできません — 変数名のみ指定できます
  4. Snippet は字句スコープ内で参照できます — 可視性はネストの階層によって決まります
  5. {@attach} は Svelte 5.29+ で使用できます — 旧バージョンでは use: action を使用します
  6. イベント属性は on で始まります — onclick は on:click ではありません(Svelte 4 の構文は非推奨です)
  7. {@attach} は setup 全体を再実行します — action とは異なり、action は再実行されません。コストの高い setup は getBar 形式でラップします
  8. class 属性の false / NaN は文字列にシリアライズされます — 5.16+ ではオブジェクト形式を使用します
  9. bind:group は同じコンポーネント内でのみ有効です — コンポーネント間では機能しません
  10. SSR 中は Transitions が実行されません — クライアントでの hydration 後に有効になります

FAQ

Q: {#each} の key はどのような役割を果たしますか? A: Svelte が効率的に差分を検出できるようにします。データが変化したとき、key によってどの要素が変化したか(挿入/移動/削除)を正確に特定し、全体の再レンダリングを防ぎます。

Q: Snippet と Slot の違いは何ですか? A: Snippet は Svelte 5 の新しい仕組みで、より強力で柔軟です。Slot ではマークアップのみを渡せますが、Snippet はパラメーターを受け取り、定義した場所で直接使用でき、コンポーネントに渡すこともでき、字句スコープを共有できます。

Q: {@html} と通常のテキスト補間の違いは何ですか? A: {expr} は HTML を自動的にエスケープします。{@html expr} は生の HTML を直接挿入します。生の HTML はスコープ付きスタイルの影響を受けません。

Q: {#key} はどのような場合に使いますか? A: 式の変化に応じてアニメーションや再作成をトリガーしたい場合に使います。たとえば、ルートの切り替えやユーザーの切り替えなどです。

Q: use: と {@attach} はどちらを選べばよいですか? A: Svelte 5.29+ では、より柔軟な {@attach}(パラメーターのリアクティブな更新に対応し、コンポーネントにも使用できます)がおすすめです。古いライブラリが action のみを提供する場合は、fromAction(act) でラップします。

Q: transition: と in: / out: の違いは何ですか? A: transition: は双方向で、逆方向にも再生できます。in: / out: は一方向で、互いに中断することなく並行して再生されます。

Q: animate: と transition: の違いは何ですか? A: animate: は keyed each 内の既存要素が並べ替えられるときに使います。transition: は要素が DOM に入る/DOM から離れるときに使います。

Q: await 式と {#await} ブロックは、どのように使い分けますか? A: 新しいコードでは await 式がおすすめです(experimental.async が必要)。同期更新と並行処理に対応しています。古いコードや単純な分岐では {#await} ブロックを使用できます。

Examples

実行可能なコード例は examples/ ディレクトリを参照してください。

ファイル内容
basic-markup.mdタグ、属性、スプレッド、テキスト式、コメント、イベント委譲
if-await-snippet.md{#if}/{#each}/{#await}/{#snippet} の基本から再帰まで
snippet-advanced.mdSnippet のスコープ、明示的/暗黙的な prop、省略可能、型付き、モジュールエクスポート、createRawSnippet
render-tags.md{@render}/{@html}/{@debug}/{@const}/{@attach}
attachments.md{@attach} factory / inline / 条件付き / action への変換
use-action.mduse: ディレクティブ + Action 型シグネチャ + use→{@attach} への移行
transitions.mdtransition:/in:/out: のパラメーター、カスタム関数、イベント、crossfade
animations.mdanimate:flip / カスタム関数 / transition との組み合わせ
await-expressions.md同期 / 並行処理 / pending / fork / SSR
style-class.mdstyle: ディレクティブ、class オブジェクト/配列、従来の class:、ClassValue
bind-directives.mdbind:value/checked/group/files/this/offsetWidth/Function bindings
event-handlers.mdonclick、イベントバブリング、委譲、window/document イベント

References

詳しい技術リファレンスは references/ ディレクトリを参照してください。

ファイル内容
basic-markup.mdタグの意味、属性のルール、イベント委譲、コメント、SSR の動作
if-await-each.md{#if}/{#else}/{#each}/{#await} の完全な構文と keyed の仕組み
snippet-render.mdSnippet の定義/呼び出し/再帰/パラメーター渡し、@render/@const の型シグネチャ
snippet-advanced.md明示的/暗黙的な prop、省略可能な Snippet、createRawSnippet、モジュールエクスポート、Snippet と Slot の違い
special-tags.md{@html}/{@debug}/{@attach}/svelte:element
attachments.md{@attach} の意味、リアクティブな再実行、fromAction への移行、svelte/attachments API
transitions.mdローカル/グローバル、組み込み transitions、カスタム関数の css/tick、events、crossfade
animations.mdanimate:flip、カスタム関数(from/to DOMRect)、transition との組み合わせ
await-expressions.md同期更新、並行処理、$effect.pending()、settled()、fork、SSR
style-class.mdstyle: の優先順位、class オブジェクト/配列/ClassValue、!important のルール
bind-reference.mdすべての bind: ディレクティブの完全な一覧と対象要素
event-reference.mdイベント処理:onclick と on:click の違い、window/document イベント

コメント