diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..9ed99ed --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,13 @@ +{ + "name": "svelte", + "owner": { + "name": "Svelte" + }, + "plugins": [ + { + "name": "svelte", + "source": "./plugins/svelte", + "description": "A plugin for all things svelte development, MCP, skills, and more." + } + ] +} diff --git a/documentation/docs/20-setup/30-remote-setup.md b/documentation/docs/20-setup/30-remote-setup.md index 108c2bf..8f4d3ad 100644 --- a/documentation/docs/20-setup/30-remote-setup.md +++ b/documentation/docs/20-setup/30-remote-setup.md @@ -16,6 +16,8 @@ claude mcp add -t http -s [scope] svelte https://mcp.svelte.dev/mcp You can choose your preferred `scope` (it must be `user`, `project` or `local`) and `name`. +If you prefer you can also install the `svelte` plugin in [the svelte claude code marketplace](plugin) that will give you both the remote server and a useful [skill](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview). + ## Claude Desktop - Open Settings > Connectors diff --git a/documentation/docs/40-claude-plugin/index.md b/documentation/docs/40-claude-plugin/index.md new file mode 100644 index 0000000..43c987a --- /dev/null +++ b/documentation/docs/40-claude-plugin/index.md @@ -0,0 +1,3 @@ +--- +title: Claude Plugin +--- diff --git a/documentation/docs/40-claude-plugin/plugin.md b/documentation/docs/40-claude-plugin/plugin.md new file mode 100644 index 0000000..e6d24b1 --- /dev/null +++ b/documentation/docs/40-claude-plugin/plugin.md @@ -0,0 +1,21 @@ +--- +title: Overview +--- + +The open source [repository](https://github.com/sveltejs/mcp) containing the code for the MCP server is also a claude code marketplace. + +The marketplace allow you to install the `svelte` plugin which will give you both the remote MCP server and a [skill](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview) to instruct the LLM on how to properly write svelte 5 code. + +## Installation + +To add the repository as a marketplace launch claude code and type + +```bash +/plugin marketplace add sveltejs/mcp +``` + +once you do that you can install the svelte skill doing + +```bash +/plugin install svelte +``` diff --git a/documentation/docs/40-claude-plugin/skill.md b/documentation/docs/40-claude-plugin/skill.md new file mode 100644 index 0000000..1f2dc79 --- /dev/null +++ b/documentation/docs/40-claude-plugin/skill.md @@ -0,0 +1,11 @@ +--- +title: Skill +--- + +Claude Skills are a set of MD files that live in your `.claude` folder (or that you can upload in your claude web/desktop). They are automatically loaded by Claude when it thinks they are appropriate for the current task. + +With those markdown files you can steer the agent behaviours and, in our case, we teach him how to properly write Svelte 5 code. The advantage over the MCP server is that the relevant tokens are only loaded when they are needed (so if you ask the LLM to write a Typescript utility in a Svelte project it will not load the skill in the context). + +You can find the skill inside the [`sveltejs/mcp`](https://github.com/sveltejs/mcp/plugins/svelte/skills) repo (it's in the `/plugins/svelte/skills` folder) and you can download it as a zip file to load it in your Claude web/desktop or to extract it inside your `.claude` folder. + +If you are using claude code you can also install it through the [svelte marketplace](plugin). diff --git a/plugins/svelte/.claude-plugin/plugin.json b/plugins/svelte/.claude-plugin/plugin.json new file mode 100644 index 0000000..5bdb514 --- /dev/null +++ b/plugins/svelte/.claude-plugin/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "svelte", + "description": "A plugin for all things svelte development, MCP, skills, and more.", + "version": "1.0.0", + "author": { + "name": "Svelte" + } +} diff --git a/plugins/svelte/.mcp.json b/plugins/svelte/.mcp.json new file mode 100644 index 0000000..0b7b5c6 --- /dev/null +++ b/plugins/svelte/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "svelte": { + "type": "http", + "url": "https://mcp.svelte.dev/mcp" + } + } +} diff --git a/plugins/svelte/skills/svelte-code-writer/SKILL.md b/plugins/svelte/skills/svelte-code-writer/SKILL.md new file mode 100644 index 0000000..ee98a3a --- /dev/null +++ b/plugins/svelte/skills/svelte-code-writer/SKILL.md @@ -0,0 +1,198 @@ +--- +name: svelte-code-writer +description: Expert guidance for writing proper Svelte 5 code with runes-based reactivity. Use when writing Svelte 5 components, migrating from Svelte 4, or troubleshooting Svelte 5 syntax. Covers $state, $derived, $effect, $props, $bindable, event handling, snippets, TypeScript integration, and common pitfalls. +--- + +# Svelte 5 Code Writer + +## Quick Reference + +Svelte 5 uses **runes** (functions starting with `$`) for explicit reactivity: + +```svelte + + + +``` + +## Core Workflow + +1. **Choose the right rune:** + - Computing from state? → Use `$derived` + - Managing reactive values? → Use `$state` + - Side effects (DOM, network, etc.)? → Use `$effect` + - Component props? → Use `$props` + +2. **Apply the pattern** (see references below for details) + +3. **Add TypeScript types** for props and state + +## Key Patterns + +### State: $state + +```svelte + +``` + +### Computed Values: $derived + +**Use `$derived` for values computed from other state** (90% of cases): + +```svelte + +``` + +### Side Effects: $effect + +**Use `$effect` only for side effects, not derivations, try to avoid reassign state in it:** + +```svelte + +``` + +### Props: $props + +```svelte + +``` + +### Two-Way Binding: $bindable + +```svelte + + + +``` + +## Event Handling + +**Events are properties** (not directives): + +```svelte + + + + + console.log(data)} /> +``` + +## Snippets (Replacing Slots) + +```svelte + + + + + + {#snippet header()} +

Title

+ {/snippet} + + Default content here +
+ +{@render header?.()} +{@render children?.()} +``` + +## Common Pitfalls + +**❌ Don't synchronize state with $effect:** + +```svelte +let doubled = $state(0); +$effect(() => { doubled = count * 2; }); // Wrong! +``` + +**✅ Use $derived instead:** + +```svelte +let doubled = $derived(count * 2); // Correct! +``` + +**❌ Don't mutate non-bindable props:** + +```svelte +let {count} = $props(); count++; // Warning! +``` + +**✅ Use callbacks or $bindable:** + +```svelte +let {(count, onIncrement)} = $props(); onIncrement(); // Correct! +``` + +## Migration from Svelte 4 + +| Svelte 4 | Svelte 5 | +| ------------------------ | ----------------------------------- | +| `let count = 0` | `let count = $state(0)` | +| `$: doubled = count * 2` | `let doubled = $derived(count * 2)` | +| `$: console.log(count)` | `$effect(() => console.log(count))` | +| `export let prop` | `let { prop } = $props()` | +| `on:click={handler}` | `onclick={handler}` | +| `` | `{@render children?.()}` | + +## Detailed References + +For comprehensive details, see: + +- **[RUNES.md](references/RUNES.md)** - Complete runes reference with examples +- **[PATTERNS.md](references/PATTERNS.md)** - Component patterns and best practices +- **[PITFALLS.md](references/PITFALLS.md)** - Common mistakes and how to fix them +- **[MIGRATION.md](references/MIGRATION.md)** - Detailed migration guide from Svelte 4 +- **[TYPESCRIPT.md](references/TYPESCRIPT.md)** - TypeScript patterns and typing + +Load these references only when you need detailed information beyond the quick reference above. diff --git a/plugins/svelte/skills/svelte-code-writer/references/MIGRATION.md b/plugins/svelte/skills/svelte-code-writer/references/MIGRATION.md new file mode 100644 index 0000000..961b0be --- /dev/null +++ b/plugins/svelte/skills/svelte-code-writer/references/MIGRATION.md @@ -0,0 +1,596 @@ +# Migration Guide: Svelte 4 → Svelte 5 + +## Overview + +Svelte 5 is largely backward compatible. You can mix Svelte 4 and 5 syntax, and migrate incrementally. + +## Migration Strategy + +1. **Update dependencies** in package.json +2. **Run migration script**: `npx sv migrate svelte-5` +3. **Fix manual migrations** (see below) +4. **Test thoroughly** +5. **Iterate component by component** + +## Automatic Migrations + +The migration script handles: + +- `let` → `$state()` +- `$:` derivations → `$derived()` +- `$:` effects → `$effect()` or `run()` +- `export let` → `$props()` +- `on:event` → `onevent` (for DOM elements) +- `` → `{@render children()}` +- Slot usage → snippets + +## Manual Migrations Required + +### 1. Component Events (createEventDispatcher) + +**Before:** + +```svelte + + + +``` + +**After:** + +```svelte + + + +``` + +**Parent usage before:** + +```svelte + +``` + +**Parent usage after:** + +```svelte + +``` + +### 2. beforeUpdate/afterUpdate + +**Before:** + +```svelte + +``` + +**After:** + +```svelte + +``` + +### 3. Component Instantiation + +**Before:** + +```js +import App from './App.svelte'; + +const app = new App({ + target: document.getElementById('app'), + props: { name: 'world' }, +}); + +app.$set({ name: 'everybody' }); +app.$on('event', callback); +app.$destroy(); +``` + +**After:** + +```js +import { mount, unmount } from 'svelte'; +import App from './App.svelte'; + +const props = $state({ name: 'world' }); +const app = mount(App, { + target: document.getElementById('app'), + props, +}); + +// Instead of $set: +props.name = 'everybody'; + +// Instead of $on: +const app = mount(App, { + target: document.getElementById('app'), + props, + events: { event: callback }, +}); + +// Instead of $destroy: +unmount(app); +``` + +### 4. Server-Side Rendering + +**Before:** + +```js +import App from './App.svelte'; + +const { html, css, head } = App.render({ name: 'world' }); +``` + +**After:** + +```js +import { render } from 'svelte/server'; +import App from './App.svelte'; + +const { html, head } = render(App, { + props: { name: 'world' }, +}); +``` + +Note: CSS is no longer returned by default. Set compiler option `css: 'injected'` if needed. + +### 5. Stores in Components + +If using stores, they still work but consider migrating to runes: + +**Before:** + +```svelte + + + +``` + +**After (using runes):** + +```svelte + + + +``` + +## Common Migration Patterns + +### Pattern 1: Reactive Statements + +**Before:** + +```svelte + +``` + +**After:** + +```svelte + +``` + +### Pattern 2: Props with Defaults + +**Before:** + +```svelte + +``` + +**After:** + +```svelte + +``` + +### Pattern 3: Slot Props + +**Before:** + +```svelte + + + + + + {user.name} is {user.age} + +``` + +**After:** + +```svelte + + + +{@render children({ name: 'Alice', age: 30 })} + + + + {#snippet children(user)} + {user.name} is {user.age} + {/snippet} + +``` + +### Pattern 4: Named Slots + +**Before:** + +```svelte + +
+
+
+ + + +

Title

+

Content

+

Footer

+
+``` + +**After:** + +```svelte + + + +
{@render header?.()}
+
{@render children?.()}
+
{@render footer?.()}
+ + + + {#snippet header()} +

Title

+ {/snippet} + +

Content

+ + {#snippet footer()} +

Footer

+ {/snippet} +
+``` + +### Pattern 5: Event Forwarding + +**Before:** + +```svelte + +``` + +**After:** + +```svelte + + + +``` + +### Pattern 6: Prop Spreading with Events + +**Before:** + +```svelte + + + +``` + +**After:** + +```svelte + + + +``` + +## The `run` Function + +The migration script may create `run()` calls for `$:` statements it can't determine are safe to convert to `$effect`: + +```svelte + +``` + +**Action required:** Review each `run()` and convert to: + +- `$derived()` if it's computing a value +- `$effect()` if it's a side effect +- Remove if no longer needed + +## Compatibility Options + +### Keep Svelte 4 Syntax Working + +Use `compatibility.componentApi: 4` to keep `new Component()` working: + +```js +// svelte.config.js +export default { + compilerOptions: { + compatibility: { + componentApi: 4, + }, + }, +}; +``` + +This adds some overhead but helps during migration. + +## Breaking Changes in Runes Mode + +### 1. Bindings Need $bindable + +**Before (Svelte 4):** + +```svelte + +``` + +All props were bindable in parent: + +```svelte + +``` + +**After (Svelte 5):** + +```svelte + +``` + +Only explicitly bindable props can use `bind:`. + +### 2. No Implicit Reactivity + +**Before:** + +```svelte + +``` + +**After:** + +```svelte + +``` + +### 3. Component Type Changes + +**Before:** + +```ts +import type { SvelteComponent } from 'svelte'; +import MyComponent from './MyComponent.svelte'; + +let instance: SvelteComponent; +let ComponentType: typeof SvelteComponent; +``` + +**After:** + +```ts +import type { Component } from 'svelte'; +import MyComponent from './MyComponent.svelte'; + +let instance: MyComponent; +let ComponentType: Component<{ prop: string }>; +``` + +## Testing Migration + +After migrating, test: + +1. **All user interactions** - clicks, forms, inputs +2. **Reactive updates** - ensure UI updates correctly +3. **Component communication** - props, events (callbacks) +4. **Lifecycle behavior** - mounting, updating, cleanup +5. **Conditional rendering** - `{#if}`, `{#each}`, `{#await}` + +## Common Migration Issues + +### Issue: Events not firing + +**Problem:** + +```svelte + +``` + +**Solution:** + +```svelte + +``` + +### Issue: Slot not rendering + +**Problem:** + +```svelte +Content +``` + +**Solution:** Update Child to use snippets: + +```svelte + + +{@render children?.()} +``` + +### Issue: Derived value not updating + +**Problem:** + +```svelte +let doubled = count * 2; +``` + +**Solution:** + +```svelte +let doubled = $derived(count * 2); +``` + +### Issue: $set no longer exists + +**Problem:** + +```js +componentInstance.$set({ prop: 'value' }); +``` + +**Solution:** Use reactive props: + +```js +const props = $state({ prop: 'value' }); +mount(Component, { props }); +props.prop = 'new value'; // Updates component +``` + +## Incremental Migration Tips + +1. **Start with leaf components** (no children) +2. **Migrate one component at a time** +3. **Test after each component** +4. **Use compatibility mode** during transition +5. **Update TypeScript types** as you go +6. **Document custom patterns** your team uses + +## Resources + +- Run `npx sv migrate svelte-5` for automated migration +- Check compiler warnings - they guide migration +- Review Svelte 5 docs: https://svelte.dev/docs +- Use VS Code extension for real-time feedback diff --git a/plugins/svelte/skills/svelte-code-writer/references/PATTERNS.md b/plugins/svelte/skills/svelte-code-writer/references/PATTERNS.md new file mode 100644 index 0000000..836df4f --- /dev/null +++ b/plugins/svelte/skills/svelte-code-writer/references/PATTERNS.md @@ -0,0 +1,553 @@ +# Component Patterns and Best Practices + +## TypeScript Integration + +### Always Use TypeScript + +```svelte + +``` + +### Generic Components + +```svelte + + +{#each items as item (item.id)} + {@render renderItem(item)} +{/each} +``` + +## State Management Patterns + +### Minimize State, Maximize Derived + +**❌ Avoid:** + +```svelte + +``` + +**✅ Better:** + +```svelte + +``` + +### Extract Reusable Reactive Logic + +Use `.svelte.js` or `.svelte.ts` files for shared state: + +```ts +// counter.svelte.ts +export function createCounter(initial = 0) { + let count = $state(initial); + let doubled = $derived(count * 2); + + return { + get count() { + return count; + }, + get doubled() { + return doubled; + }, + increment: () => count++, + decrement: () => count--, + }; +} +``` + +```svelte + + + + +``` + +### Class-Based Reactive State + +```svelte + +``` + +## Component Composition + +### Snippet-Based Composition + +```svelte + + + + + {#if header} + {@render header()} + {/if} + + + {#each data as item} + {@render row(item)} + {/each} + + + {#if footer} + {@render footer()} + {/if} +
+``` + +Usage: + +```svelte + + {#snippet header()} + + + + + {/snippet} + + {#snippet row(person)} + + + {/snippet} + + {#snippet footer()} + + {/snippet} +
NameAge
{person.name}{person.age}
Total: {data.length}
+``` + +### Higher-Order Components Pattern + +```svelte + + + +{#if isAuthenticated} + +{:else} +

Please log in

+{/if} +``` + +## Event Handling Patterns + +### Named Event Handlers + +```svelte + + + + + +``` + +### Event Delegation Pattern + +For many similar elements: + +```svelte + + +
+ {#each items as item} + + {/each} +
+``` + +### Custom Event Handlers (Callbacks) + +```svelte + + +``` + +## Form Patterns + +### Two-Way Form Binding + +```svelte + + +
+ + + + +
+``` + +### Custom Form Components + +```svelte + + + + +``` + +## Async Patterns + +### Loading States + +```svelte + + +{#await promise} +

Loading...

+{:then data} + +{:catch error} +

Error: {error.message}

+{/await} +``` + +### Manual Loading State + +```svelte + + +{#if loading} +

Loading...

+{:else if error} +

Error: {error.message}

+{:else if data} + +{/if} +``` + +## Styling Patterns + +### Dynamic Classes + +```svelte + + + +
Content
+ + +
+ Content +
+ + +
Content
+``` + +### Component Style Props + +```svelte + + + + + + +``` + +## Performance Patterns + +### Use Keys in Lists + +```svelte +{#each items as item (item.id)} + +{/each} +``` + +### Use $state.raw for Large Non-Reactive Data + +```svelte + +``` + +### Lazy Loading Components + +```svelte + + +{#if show && HeavyComponent} + +{/if} +``` + +## Testing Patterns + +```typescript +import { mount } from 'svelte'; +import { expect, test } from 'vitest'; +import Component from './Component.svelte'; + +test('increments counter', async () => { + const target = document.createElement('div'); + const instance = mount(Component, { target }); + + const button = target.querySelector('button'); + button?.click(); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(target.textContent).toContain('1'); +}); +``` + +## Accessibility Patterns + +```svelte + + + + + +``` diff --git a/plugins/svelte/skills/svelte-code-writer/references/PITFALLS.md b/plugins/svelte/skills/svelte-code-writer/references/PITFALLS.md new file mode 100644 index 0000000..b98cb1a --- /dev/null +++ b/plugins/svelte/skills/svelte-code-writer/references/PITFALLS.md @@ -0,0 +1,390 @@ +# Common Pitfalls + +## 1. Using $effect Instead of $derived + +**❌ Wrong:** + +```svelte + +``` + +**✅ Correct:** + +```svelte + +``` + +## 2. Mutating Non-Bindable Props + +**❌ Wrong:** + +```svelte + +``` + +**✅ Correct Option 1 - Callback:** + +```svelte + + + +``` + +**✅ Correct Option 2 - $bindable:** + +```svelte + + + + + + +``` + +## 3. Accessing Undefined DOM References + +**❌ Wrong:** + +```svelte + + + +``` + +**✅ Correct:** + +```svelte + + + +``` + +## 4. Missing Keys in Lists + +**❌ Wrong:** + +```svelte +{#each items as item} +
{item.name}
+{/each} +``` + +**✅ Correct:** + +```svelte +{#each items as item (item.id)} +
{item.name}
+{/each} +``` + +## 5. Using on: Event Directive + +**❌ Wrong (Svelte 4 syntax):** + +```svelte + + +``` + +**✅ Correct:** + +```svelte + + +``` + +## 6. Using export let for Props + +**❌ Wrong (Svelte 4 syntax):** + +```svelte + +``` + +**✅ Correct:** + +```svelte + +``` + +## 7. Using Instead of Snippets + +**❌ Wrong (deprecated):** + +```svelte + + + +``` + +**✅ Correct:** + +```svelte + + + +{@render header?.()} +{@render children?.()} +``` + +## 8. Using createEventDispatcher + +**❌ Wrong (deprecated):** + +```svelte + +``` + +**✅ Correct:** + +```svelte + +``` + +## 9. Not Handling Undefined Snippets + +**❌ Wrong:** + +```svelte + + +{@render header()} +``` + +**✅ Correct:** + +```svelte + + +{@render header?.()} + + + +{#if header} + {@render header()} +{:else} +

Default Header

+{/if} +``` + +## 10. Destructuring Props Without Types + +**❌ Wrong:** + +```svelte + +``` + +**✅ Correct:** + +```svelte + +``` + +## 11. Making Everything Bindable + +**❌ Wrong:** + +```svelte + +``` + +Only use `$bindable()` when truly needed for two-way binding. + +**✅ Correct:** + +```svelte + +``` + +## 12. Using Plain let for Reactive Values + +**❌ Wrong:** + +```svelte + + + +``` + +**✅ Correct:** + +```svelte + + + +``` + +## 13. Infinite Effect Loops + +**❌ Wrong:** + +```svelte + +``` + +Use `untrack` if you must read without tracking: + +```svelte + +``` + +## 14. Forgetting Cleanup in Effects + +**❌ Wrong:** + +```svelte + +``` + +**✅ Correct:** + +```svelte + +``` + +## 15. Using + +**❌ Wrong (unnecessary):** + +```svelte + + + +``` + +**✅ Correct:** + +```svelte + + + +``` diff --git a/plugins/svelte/skills/svelte-code-writer/references/RUNES.md b/plugins/svelte/skills/svelte-code-writer/references/RUNES.md new file mode 100644 index 0000000..1a34ad1 --- /dev/null +++ b/plugins/svelte/skills/svelte-code-writer/references/RUNES.md @@ -0,0 +1,367 @@ +# Runes Reference + +## $state + +Create reactive state that updates the UI when changed. + +### Basic Usage + +```svelte + +``` + +### Deep Reactivity + +Objects and arrays become deeply reactive proxies: + +```svelte + +``` + +### In Classes + +```svelte + +``` + +### $state.raw + +Skip deep reactivity for performance: + +```svelte + +``` + +### $state.snapshot + +Get non-reactive snapshot: + +```svelte + +``` + +## $derived + +Compute values from other state. **Always prefer $derived over $effect for computations.** + +### Basic Usage + +```svelte + +``` + +### $derived.by + +For complex computations: + +```svelte + +``` + +### Dependency Tracking + +Dependencies are tracked at runtime: + +```svelte + +``` + +### Overriding Derived Values + +Since Svelte 5.25, deriveds can be reassigned temporarily: + +```svelte + +``` + +## $effect + +Run side effects when dependencies change. **Use sparingly - most "effects" should be $derived.** + +### Basic Pattern + +```svelte + +``` + +### Timing + +Effects run: + +- After component mounts +- After state changes (in microtask) +- After DOM updates + +### Common Use Cases + +**Canvas drawing:** + +```svelte + + + +``` + +**Intervals/timers:** + +```svelte + +``` + +**Network requests:** + +```svelte + +``` + +### $effect.pre + +Run before DOM updates: + +```svelte + +``` + +### Dependency Management + +Dependencies are tracked automatically: + +```svelte + +``` + +To exempt something from tracking, use `untrack`: + +```svelte + +``` + +## $props + +Receive data from parent components. + +### Basic Pattern + +```svelte + +``` + +### Rest Props + +```svelte + + +
{name}
+``` + +### Renaming + +```svelte + +``` + +### Generic Props + +```svelte + +``` + +## $bindable + +Make props two-way bindable. + +### Basic Pattern + +```svelte + + + +``` + +### With Fallback + +Fallback is used when prop is not bound: + +```svelte + + + + + + + + + +``` + +### Function Bindings + +```svelte + + + + value, (v) => (value = v.toUpperCase())} /> +``` diff --git a/plugins/svelte/skills/svelte-code-writer/references/TYPESCRIPT.md b/plugins/svelte/skills/svelte-code-writer/references/TYPESCRIPT.md new file mode 100644 index 0000000..e7a987f --- /dev/null +++ b/plugins/svelte/skills/svelte-code-writer/references/TYPESCRIPT.md @@ -0,0 +1,652 @@ +# TypeScript Patterns + +## Basic Setup + +### Component with Types + +```svelte + +``` + +### Inline Types + +For simple components: + +```svelte + +``` + +## Typing $state + +### Basic State + +```svelte + +``` + +### State with Undefined + +```svelte + +``` + +### Arrays and Objects + +```svelte + +``` + +### Class State + +```svelte + +``` + +## Typing $derived + +```svelte + +``` + +## Typing $props + +### Basic Props Interface + +```svelte + +``` + +### Generic Props + +```svelte + + +{#each items as item (item.id)} + +{/each} +``` + +### Multiple Generics + +```svelte + + +

{data[key]}

+``` + +### Generic with Constraints + +```svelte + + +{#each keys as key} +
{key}: {data[key]}
+{/each} +``` + +## Typing Snippets + +### Basic Snippet + +```svelte + + +{@render children?.()} +``` + +### Snippet with Parameters + +```svelte + + +{#each users as user} + {@render row(user)} +{/each} +``` + +### Multiple Parameters + +```svelte + + +{@render cell(0, 0, 'A1')} +``` + +### Optional Snippets + +```svelte + + +{@render header?.()} +
Content
+{@render footer?.()} +``` + +## Typing $bindable + +```svelte + +``` + +## Component Types + +### Component Type + +```svelte + + + +``` + +### ComponentProps Type + +Extract props from a component: + +```svelte + + + +``` + +### Component Instance Type + +```svelte + + + +``` + +## Event Handler Types + +### DOM Events + +```svelte + + + + +
...
+``` + +### Custom Event Callbacks + +```svelte + +``` + +## Typing with HTML Elements + +### Element References + +```svelte + + +
Content
+ + +``` + +### HTML Attributes + +Extend HTML attributes for custom elements: + +```ts +// app.d.ts +import type { HTMLAttributes } from 'svelte/elements'; + +declare module 'svelte/elements' { + export interface SvelteHTMLElements { + 'custom-element': HTMLAttributes & { + 'custom-prop'?: string; + }; + } +} +``` + +## Typing Actions + +```svelte + + +
{} }}>Content
+``` + +## Wrapper Components + +### Extending HTML Element Props + +```svelte + + + +``` + +### Generic Wrapper + +```svelte + + + + {@render children?.()} + +``` + +## Store Types (Legacy) + +If still using stores: + +```svelte + + +{#if $user} +

{$user.name}

+{/if} +``` + +## Advanced Patterns + +### Discriminated Unions + +```svelte + + +{#if state.status === 'loading'} +

Loading...

+{:else if state.status === 'success'} + +{:else if state.status === 'error'} +

Error: {state.error.message}

+{/if} +``` + +### Conditional Props + +```svelte + + +{#if props.mode === 'edit'} + +{/if} +``` + +### Type Guards + +```svelte + +``` + +## tsconfig.json Settings + +Recommended settings: + +```json +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} +``` + +## Common Type Issues + +### Issue: Type not inferred in $derived + +```svelte + +``` + +### Issue: Snippet parameter types + +```svelte + +``` + +### Issue: Generic component props + +```svelte + +```