Compare commits

...

45 Commits

Author SHA1 Message Date
github-actions[bot]
2ded13539a Version Packages (#192)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-10 18:39:19 +02:00
paoloricciuti
96c50acae2 fix: add server export to opencode plugin 2026-04-10 18:37:49 +02:00
github-actions[bot]
4920088e5c Version Packages (#191)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-10 16:57:38 +02:00
Paolo Ricciuti
8557f0af6f fix: import ts files directly (#190) 2026-04-10 16:56:23 +02:00
renovate[bot]
972d0f0bb2 chore(deps): update dependency @vercel/analytics to v2 (#177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-27 10:15:32 +01:00
renovate[bot]
7bf364a9d5 chore(deps): update pnpm/action-setup action to v5 (#186)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-27 10:14:49 +01:00
Paolo Ricciuti
d06d758b81 chore: sync agents with the opencode plugin too (#187) 2026-03-25 09:43:52 +01:00
github-actions[bot]
e5ce7437c4 Version Packages (#179)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-13 23:07:32 +01:00
Paolo Ricciuti
2f422ee190 fix: broaden checks for imported runes because LLMs are unhinged (#185) 2026-03-13 22:59:52 +01:00
paoloricciuti
14f087cd7a chore: simplify sync script/action 2026-03-13 22:22:19 +01:00
Chris Tsongas
1ef5ddf605 fix: update svelte-file-editor agent to use proper name (#183)
Co-authored-by: paoloricciuti <ricciutipaolo@gmail.com>
2026-03-13 10:37:43 +01:00
github-actions[bot]
0e55ee792d chore: sync skills from svelte.dev (#178)
Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com>
Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com>
2026-03-12 18:32:21 +01:00
paoloricciuti
84ec24b6f6 fix: don't format code blocks in references 2026-03-12 18:19:43 +01:00
paoloricciuti
710cebe539 docs: opencode config link 2026-03-12 17:59:32 +01:00
paoloricciuti
b2a380c4ce docs: fix markdown blocks 2026-03-12 17:57:45 +01:00
paoloricciuti
eef0a9b4d9 docs: update opencode docs 2026-03-11 21:31:43 +01:00
Paolo Ricciuti
260b36e8af chore: undo rename for publish to registry check 2026-03-11 14:11:15 +01:00
github-actions[bot]
6df3ebe568 Version Packages (#173)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-11 13:08:21 +01:00
Momcilo Miladinovic
27a2fc5653 fix: merge user-configured svelte-file-editor agent settings (#176)
Co-authored-by: Momochiro <x@Mac.sweet.opnsense>
Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com>
2026-03-11 12:59:01 +01:00
Paolo Ricciuti
5cd99d8234 feat: allow enabling a specific skill in opencode plugin (#174) 2026-03-10 17:38:04 +01:00
Paolo Ricciuti
e9f19199cb feat: add sync skill docs (#172) 2026-03-09 17:49:18 +01:00
Willow (GHOST)
480b46df0a feat(opencode): mcp enabled option is passed to opencode (#171)
Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com>
2026-03-07 12:38:45 +01:00
jyc.dev
5f5fb27977 feat: display similar result & error at the end (#161) 2026-03-07 12:20:18 +01:00
Paolo Ricciuti
29cfa77c39 chore: restructure repo to have single source of truth for tools (#170) 2026-03-03 21:43:34 +01:00
github-actions[bot]
249923b1e5 Version Packages (#169)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 22:24:34 +01:00
Paolo Ricciuti
8c67cae90f feat: svelte-core-bestpractices skill (#162) 2026-02-27 22:19:11 +01:00
Paolo Ricciuti
4aad2e0cfe fix: rename repos in file (#168) 2026-02-27 20:01:15 +01:00
github-actions[bot]
02935b00a6 Version Packages (#164)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-22 15:05:33 +01:00
Paolo Ricciuti
1ea98adacd feat: cursor plugin (#166) 2026-02-22 15:02:31 +01:00
Paolo Ricciuti
494409cc42 fix: better subagent instructions to use MCP or skill (#163) 2026-02-11 22:57:09 +01:00
Paolo Ricciuti
46d8f6cce8 fix: only run "Publish to MCP registry" when MCP is published (#160) 2026-02-07 22:50:08 +01:00
github-actions[bot]
8dc63dca08 chore: sync skills and update documentation (#158)
Co-authored-by: paoloricciuti <26281609+paoloricciuti@users.noreply.github.com>
2026-02-06 18:41:18 +01:00
paoloricciuti
19fedcd35f docs: add instructions to default schema` 2026-02-06 18:40:55 +01:00
István Pató
b4eb5cc960 Update description for svelte-code-writer skill (#149) 2026-02-06 18:37:40 +01:00
paoloricciuti
6676fd8116 docs: add local opencode configuration 2026-02-06 18:37:03 +01:00
github-actions[bot]
a755a33a5f Version Packages (#157)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-06 17:30:56 +01:00
Paolo Ricciuti
556f96cfaf feat: allow for local opencode config (#156) 2026-02-06 17:25:33 +01:00
github-actions[bot]
77a3340c2f chore: sync AGENTS.md to opencode package and documentation (#155)
Co-authored-by: paoloricciuti <26281609+paoloricciuti@users.noreply.github.com>
2026-02-06 17:11:38 +01:00
Paolo Ricciuti
9ac8fd51e7 chore: sync AGENTS.md with opencode and docs (#154)
Co-authored-by: jyc.dev <jycouet@gmail.com>
2026-02-06 17:10:42 +01:00
paoloricciuti
c764308d79 fix: update version of claude plugin 2026-01-31 10:02:31 +01:00
paoloricciuti
01a7e6a8d3 fix: correct command for svelte language server 2026-01-31 09:52:57 +01:00
Paolo Ricciuti
d8ed686e3a chore: use @include in docs (#152) 2026-01-30 16:30:52 -05:00
github-actions[bot]
6f0390d0a9 Version Packages (#153)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-30 16:23:09 +01:00
paoloricciuti
c2c1b3e5e7 fix: actually push skills to right config path 2026-01-30 16:20:36 +01:00
github-actions[bot]
cdfbb907b6 Version Packages (#147)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-30 16:03:12 +01:00
103 changed files with 6737 additions and 584 deletions

View File

@@ -1,6 +1,6 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": ["@svitejs/changesets-changelog-github-compact", { "repo": "sveltejs/mcp" }],
"changelog": ["@svitejs/changesets-changelog-github-compact", { "repo": "sveltejs/ai-tools" }],
"commit": false,
"fixed": [],
"linked": [],

View File

@@ -1,5 +0,0 @@
---
'@sveltejs/mcp': patch
---
fix: turn off no-inspect in eslint for mcp

View File

@@ -1,5 +0,0 @@
---
'@sveltejs/opencode': minor
---
feat: distribute skills through opencode plugin

View File

@@ -6,11 +6,11 @@
"plugins": [
{
"name": "svelte",
"source": "./plugins/svelte",
"source": "./plugins/claude/svelte",
"description": "A plugin for all things Svelte development, MCP, skills, and more.",
"lspServers": {
"svelte": {
"command": "svelte-language-server",
"command": "svelteserver",
"args": ["--stdio"],
"extensionToLanguage": {
".svelte": "svelte"

View File

@@ -0,0 +1,13 @@
{
"name": "svelte",
"owner": {
"name": "Svelte"
},
"plugins": [
{
"name": "svelte",
"source": "./plugins/cursor/svelte",
"description": "A plugin for all things Svelte development, MCP, skills, and more."
}
]
}

View File

@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v5
with:
version: 10.28.2

View File

@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v5
with:
version: 10.28.2

View File

@@ -1,23 +1,44 @@
name: Release Svelte Code Writer Skill
name: Release Svelte Skills
on:
push:
branches:
- main
paths:
- 'plugins/svelte/skills/svelte-code-writer/**'
- 'tools/skills/**'
workflow_dispatch:
permissions: {}
jobs:
detect-skills:
permissions:
contents: read
if: github.repository == 'sveltejs/ai-tools'
name: Detect changed skills
runs-on: ubuntu-latest
outputs:
skills: ${{ steps.find-skills.outputs.skills }}
steps:
- name: checkout
uses: actions/checkout@v6
- name: Find all skills
id: find-skills
run: |
skills=$(ls -d tools/skills/*/ | xargs -I {} basename {} | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "skills=$skills" >> $GITHUB_OUTPUT
release:
needs: detect-skills
if: needs.detect-skills.outputs.skills != '[]'
permissions:
contents: write
# prevents this action from running on forks
if: github.repository == 'sveltejs/mcp'
name: Release Svelte Code Writer Skill
name: Release ${{ matrix.skill }}
runs-on: ubuntu-latest
strategy:
matrix:
skill: ${{ fromJson(needs.detect-skills.outputs.skills) }}
steps:
- name: checkout
uses: actions/checkout@v6
@@ -28,18 +49,18 @@ jobs:
- name: Create zip
run: |
cd plugins/svelte/skills
zip -r svelte-code-writer.zip svelte-code-writer/
cd tools/skills
zip -r ${{ matrix.skill }}.zip ${{ matrix.skill }}/
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: svelte-code-writer-v${{ steps.version.outputs.version }}
name: Svelte Code Writer Skill v${{ steps.version.outputs.version }}
tag_name: ${{ matrix.skill }}-v${{ steps.version.outputs.version }}
name: ${{ matrix.skill }} v${{ steps.version.outputs.version }}
body: |
Automated release of the Svelte Code Writer skill.
Automated release of the ${{ matrix.skill }} skill.
This release was triggered by changes to the `plugins/svelte/skills/svelte-code-writer/` directory.
files: plugins/svelte/skills/svelte-code-writer.zip
This release was triggered by changes to the `tools/skills/${{ matrix.skill }}/` directory.
files: tools/skills/${{ matrix.skill }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -13,11 +13,11 @@ jobs:
id-token: write # OpenID Connect token needed for provenance
pull-requests: write # to create pull request (changesets/action)
# prevents this action from running on forks
if: github.repository == 'sveltejs/mcp'
if: github.repository == 'sveltejs/ai-tools'
name: Release
runs-on: ${{ matrix.os }}
outputs:
published: ${{ steps.changesets.outputs.published }}
publishedPackages: ${{ steps.changesets.outputs.publishedPackages }}
strategy:
matrix:
# pseudo-matrix for convenience, NEVER use more than a single combination
@@ -64,7 +64,7 @@ jobs:
publish-mcp:
needs: release
if: needs.release.outputs.published == 'true'
if: contains(needs.release.outputs.publishedPackages, '"@sveltejs/mcp"')
uses: ./.github/workflows/publish-mcp.yml
secrets:
MCP_KEY: ${{ secrets.MCP_KEY }}

116
.github/workflows/sync-docs-skills.yml vendored Normal file
View File

@@ -0,0 +1,116 @@
name: Sync Skills
on:
workflow_dispatch:
permissions:
contents: write
pull-requests: write
actions: write
jobs:
sync-skills:
if: github.repository == 'sveltejs/ai-tools'
name: Sync skills from svelte.dev
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 24
package-manager-cache: false # pnpm is not installed yet
- name: Install pnpm
shell: bash
run: |
PNPM_VER=$(jq -r '.packageManager | if .[0:5] == "pnpm@" then .[5:] else "packageManager in package.json does not start with pnpm@\n" | halt_error(1) end' package.json)
echo installing pnpm version "$PNPM_VER"
npm i -g "pnpm@$PNPM_VER"
- name: Setup Node.js with pnpm cache
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 24
package-manager-cache: true # caches pnpm via packageManager field in package.json
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts
- name: Clone svelte.dev
run: git clone --depth 2 https://github.com/sveltejs/svelte.dev.git "${{ runner.temp }}/svelte.dev"
- name: Discover changed skill files
id: discover
env:
SVELTE_DEV_ROOT: ${{ runner.temp }}/svelte.dev
run: |
skill_files=$(git -C "$SVELTE_DEV_ROOT" diff --name-only --diff-filter=ACMR HEAD~1 HEAD | grep '^apps/svelte.dev/content/docs/.*\.md$' | xargs -I{} grep -l '^skill: *true' "$SVELTE_DEV_ROOT/{}" || true)
echo "skill_files=$skill_files" >> "$GITHUB_OUTPUT"
- name: Sync skills
if: steps.discover.outputs.skill_files != ''
env:
SVELTE_DEV_ROOT: ${{ runner.temp }}/svelte.dev
DOCS_PREFIX: apps/svelte.dev/content/docs/
run: |
for full_path in ${{ steps.discover.outputs.skill_files }}; do
file="${full_path#$SVELTE_DEV_ROOT/}"
name=$(grep '^name: ' "$full_path" | head -1 | sed 's/^name: *//')
repo="${file#$DOCS_PREFIX}"
repo="${repo#/}"
repo="${repo%%/*}"
output_dir="tools/skills/$name"
rm -rf "$output_dir"
mkdir -p "$output_dir"
pnpm resolve-references --file "$full_path" --repo "$repo" --output "$output_dir"
done
- name: Sync plugins
if: steps.discover.outputs.skill_files != ''
run: |
pnpm sync-claude-plugin
pnpm sync-cursor-plugin
pnpm sync-opencode-plugin
pnpm generate-skill-docs
pnpm bump-plugin-versions
- name: Check for changes
id: git-check
run: |
git diff --exit-code -- tools/skills/ plugins/ packages/opencode/ documentation/docs/ || echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Create Pull Request
if: steps.git-check.outputs.changed == 'true'
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: sync skills from svelte.dev'
branch: chore/sync-skills
delete-branch: true
title: 'chore: sync skills from svelte.dev'
body: |
## Summary
Automatically synced skill markdown from `sveltejs/svelte.dev` into `tools/skills/`.
## Changes
- Cloned `sveltejs/svelte.dev`
- Filtered markdown files with `skill: true` frontmatter
- Rebuilt synced skill folders with `scripts/resolve-references.ts`
- Synced `plugins/claude/svelte/` (skills, agents)
- Synced `plugins/cursor/svelte/` (skills, agents, rules)
- Synced `packages/opencode/` (skills, instructions)
- Updated documentation
## Generated by
GitHub Action: Sync Skills
labels: |
chore
automated

View File

@@ -1,21 +1,21 @@
name: Sync OpenCode Skills
name: Sync Plugins
on:
push:
branches:
- main
paths:
- 'plugins/svelte/skills/**'
- 'tools/**'
permissions:
contents: write
pull-requests: write
jobs:
sync-skills:
sync-plugins:
# prevents this action from running on forks
if: github.repository == 'sveltejs/mcp'
name: Sync Skills to OpenCode Package and Update Docs
if: github.repository == 'sveltejs/ai-tools'
name: Sync Plugins from tools/
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -46,8 +46,8 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts
- name: Sync skills folder
run: pnpm sync-opencode-skills
- name: Sync plugins
run: pnpm sync-plugins
- name: Generate skills documentation
run: pnpm generate-skill-docs
@@ -55,30 +55,38 @@ jobs:
- name: Check for changes
id: git-check
run: |
git diff --exit-code packages/opencode/skills documentation/docs/60-skills/10-skills.md || echo "changed=true" >> $GITHUB_OUTPUT
git diff --exit-code \
plugins/claude/svelte/ \
plugins/cursor/svelte/ \
packages/opencode/skills/ \
packages/opencode/instructions/ \
packages/opencode/schema.json \
documentation/docs/ \
|| echo "changed=true" >> $GITHUB_OUTPUT
- name: Create Pull Request
if: steps.git-check.outputs.changed == 'true'
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: sync skills and update documentation'
branch: chore/sync-opencode-skills
commit-message: 'chore: sync plugins from tools/'
branch: chore/sync-plugins
delete-branch: true
title: 'chore: sync skills and update documentation'
title: 'chore: sync plugins from tools/'
body: |
## Summary
Automatically synced skills and updated documentation.
Automatically synced all plugins from the `tools/` source of truth.
This PR was triggered by changes to the skills folder in `plugins/svelte/skills/`.
This PR was triggered by changes to `tools/**`.
## Changes
- Synced `packages/opencode/skills/` with latest skill definitions
- Updated `documentation/docs/60-skills/10-skills.md` with latest skill documentation
- Synced `plugins/claude/svelte/` (skills, agents with `permissionMode`)
- Synced `plugins/cursor/svelte/` (skills, agents, rules)
- Synced `packages/opencode/` (skills, instructions, schema)
- Updated documentation
## Generated by
GitHub Action: Sync OpenCode Skills
GitHub Action: Sync Plugins
labels: |
chore
documentation
automated

View File

@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v5
with:
version: 10.28.2

View File

@@ -14,7 +14,7 @@ permissions:
jobs:
update-docs:
# prevents this action from running on forks
if: github.repository == 'sveltejs/mcp'
if: github.repository == 'sveltejs/ai-tools'
name: Update OpenCode JSON Schema
runs-on: ubuntu-latest
steps:

View File

@@ -14,7 +14,7 @@ permissions:
jobs:
update-docs:
# prevents this action from running on forks
if: github.repository == 'sveltejs/mcp'
if: github.repository == 'sveltejs/ai-tools'
name: Update Prompt Documentation
runs-on: ubuntu-latest
steps:

View File

@@ -10,6 +10,12 @@
"options": {
"parser": "svelte"
}
},
{
"files": "**/references/*.md",
"options": {
"embeddedLanguageFormatting": "off"
}
}
]
}

View File

@@ -0,0 +1,23 @@
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available Svelte MCP Tools:
### 1. list-sections
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
### 2. get-documentation
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
### 3. svelte-autofixer
Analyzes Svelte code and returns issues and suggestions.
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
### 4. playground-link
Generates a Svelte Playground link with the provided code.
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.

View File

@@ -17,30 +17,10 @@ To get the most out of the MCP server we recommend including the following promp
> [!NOTE] This is already setup for you when using `npx sv add mcp`
```md
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available MCP Tools:
### 1. list-sections
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
### 2. get-documentation
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
### 3. svelte-autofixer
Analyzes Svelte code and returns issues and suggestions.
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
### 4. playground-link
Generates a Svelte Playground link with the provided code.
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.
```
<!-- prettier-ignore-start -->
````markdown
@include .generated/agents.md
````
<!-- prettier-ignore-end -->
If your MCP client supports it, we also recommend using the [svelte-task](prompts#svelte-task) prompt to instruct the LLM on the best way to use the MCP server.

View File

@@ -0,0 +1,213 @@
## svelte-task
This prompt should be used whenever you are asking the model to work on a Svelte-related task. It will instruct the LLM which documentation sections are available, which tools to invoke, when to invoke them, and how to interpret the results.
<details>
<summary>Copy the prompt</summary>
<!-- prettier-ignore-start -->
````markdown
You are a Svelte expert tasked to build components and utilities for Svelte developers. If you need documentation for anything related to Svelte you can invoke the tool `get-documentation` with one of the following paths. However: before invoking the `get-documentation` tool, try to answer the users query using your own knowledge and the `svelte-autofixer` tool. Be mindful of how many section you request, since it is token-intensive!
<available-docs>
- title: Overview, use_cases: use title and path to estimate use case, path: ai/overview
- title: Local setup, use_cases: use title and path to estimate use case, path: ai/local-setup
- title: Remote setup, use_cases: use title and path to estimate use case, path: ai/remote-setup
- title: Tools, use_cases: use title and path to estimate use case, path: ai/tools
- title: Resources, use_cases: use title and path to estimate use case, path: ai/resources
- title: Prompts, use_cases: use title and path to estimate use case, path: ai/prompts
- title: Overview, use_cases: use title and path to estimate use case, path: ai/plugin
- title: Subagent, use_cases: use title and path to estimate use case, path: ai/subagent
- title: Overview, use_cases: use title and path to estimate use case, path: ai/opencode-plugin
- title: Subagent, use_cases: use title and path to estimate use case, path: ai/opencode-subagent
- title: Overview, use_cases: use title and path to estimate use case, path: ai/skills
- title: Overview, use_cases: project setup, creating new svelte apps, scaffolding, cli tools, initializing projects, path: cli/overview
- title: Frequently asked questions, use_cases: project setup, initializing new svelte projects, troubleshooting cli installation, package manager configuration, path: cli/faq
- title: sv create, use_cases: project setup, starting new sveltekit app, initializing project, creating from playground, choosing project template, path: cli/sv-create
- title: sv add, use_cases: project setup, adding features to existing projects, integrating tools, testing setup, styling setup, authentication, database setup, deployment adapters, path: cli/sv-add
- title: sv check, use_cases: code quality, ci/cd pipelines, error checking, typescript projects, pre-commit hooks, finding unused css, accessibility auditing, production builds, path: cli/sv-check
- title: sv migrate, use_cases: migration, upgrading svelte versions, upgrading sveltekit versions, modernizing codebase, svelte 3 to 4, svelte 4 to 5, sveltekit 1 to 2, adopting runes, refactoring deprecated apis, path: cli/sv-migrate
- title: devtools-json, use_cases: development setup, chrome devtools integration, browser-based editing, local development workflow, debugging setup, path: cli/devtools-json
- title: drizzle, use_cases: database setup, sql queries, orm integration, data modeling, postgresql, mysql, sqlite, server-side data access, database migrations, type-safe queries, path: cli/drizzle
- title: eslint, use_cases: code quality, linting, error detection, project setup, code standards, team collaboration, typescript projects, path: cli/eslint
- title: better-auth, use_cases: use title and path to estimate use case, path: cli/better-auth
- title: mcp, use_cases: use title and path to estimate use case, path: cli/mcp
- title: mdsvex, use_cases: blog, content sites, markdown rendering, documentation sites, technical writing, cms integration, article pages, path: cli/mdsvex
- title: paraglide, use_cases: internationalization, multi-language sites, i18n, translation, localization, language switching, global apps, multilingual content, path: cli/paraglide
- title: playwright, use_cases: browser testing, e2e testing, integration testing, test automation, quality assurance, ci/cd pipelines, testing user flows, path: cli/playwright
- title: prettier, use_cases: code formatting, project setup, code style consistency, team collaboration, linting configuration, path: cli/prettier
- title: storybook, use_cases: component development, design systems, ui library, isolated component testing, documentation, visual testing, component showcase, path: cli/storybook
- title: sveltekit-adapter, use_cases: deployment, production builds, hosting setup, choosing deployment platform, configuring adapters, static site generation, node server, vercel, cloudflare, netlify, path: cli/sveltekit-adapter
- title: tailwindcss, use_cases: project setup, styling, css framework, rapid prototyping, utility-first css, design systems, responsive design, adding tailwind to svelte, path: cli/tailwind
- title: vitest, use_cases: testing, unit tests, component testing, test setup, quality assurance, ci/cd pipelines, test-driven development, path: cli/vitest
- title: add-on, use_cases: use title and path to estimate use case, path: cli/add-on
- title: sv-utils, use_cases: use title and path to estimate use case, path: cli/sv-utils
- title: Introduction, use_cases: learning sveltekit, project setup, understanding framework basics, choosing between svelte and sveltekit, getting started with full-stack apps, path: kit/introduction
- title: Creating a project, use_cases: project setup, starting new sveltekit app, initial development environment, first-time sveltekit users, scaffolding projects, path: kit/creating-a-project
- title: Project types, use_cases: deployment, project setup, choosing adapters, ssg, spa, ssr, serverless, mobile apps, desktop apps, pwa, offline apps, browser extensions, separate backend, docker containers, path: kit/project-types
- title: Project structure, use_cases: project setup, understanding file structure, organizing code, starting new project, learning sveltekit basics, path: kit/project-structure
- title: Web standards, use_cases: always, any sveltekit project, data fetching, forms, api routes, server-side rendering, deployment to various platforms, path: kit/web-standards
- title: Routing, use_cases: routing, navigation, multi-page apps, project setup, file structure, api endpoints, data loading, layouts, error pages, always, path: kit/routing
- title: Loading data, use_cases: data fetching, api calls, database queries, dynamic routes, page initialization, loading states, authentication checks, ssr data, form data, content rendering, path: kit/load
- title: Form actions, use_cases: forms, user input, data submission, authentication, login systems, user registration, progressive enhancement, validation errors, path: kit/form-actions
- title: Page options, use_cases: prerendering static sites, ssr configuration, spa setup, client-side rendering control, url trailing slash handling, adapter deployment config, build optimization, path: kit/page-options
- title: State management, use_cases: sveltekit, server-side rendering, ssr, state management, authentication, data persistence, load functions, context api, navigation, component lifecycle, path: kit/state-management
- title: Remote functions, use_cases: data fetching, server-side logic, database queries, type-safe client-server communication, forms, user input, mutations, authentication, crud operations, optimistic updates, path: kit/remote-functions
- title: Building your app, use_cases: production builds, deployment preparation, build process optimization, adapter configuration, preview before deployment, path: kit/building-your-app
- title: Adapters, use_cases: deployment, production builds, hosting setup, choosing deployment platform, configuring adapters, path: kit/adapters
- title: Zero-config deployments, use_cases: deployment, production builds, hosting setup, choosing deployment platform, ci/cd configuration, path: kit/adapter-auto
- title: Node servers, use_cases: deployment, production builds, node.js hosting, custom server setup, environment configuration, reverse proxy setup, docker deployment, systemd services, path: kit/adapter-node
- title: Static site generation, use_cases: static site generation, ssg, prerendering, deployment, github pages, spa mode, blogs, documentation sites, marketing sites, path: kit/adapter-static
- title: Single-page apps, use_cases: spa mode, single-page apps, client-only rendering, static hosting, mobile app wrappers, no server-side logic, adapter-static setup, fallback pages, path: kit/single-page-apps
- title: Cloudflare, use_cases: deployment, cloudflare workers, cloudflare pages, hosting setup, production builds, serverless deployment, edge computing, path: kit/adapter-cloudflare
- title: Cloudflare Workers, use_cases: deploying to cloudflare workers, cloudflare workers sites deployment, legacy cloudflare adapter, wrangler configuration, cloudflare platform bindings, path: kit/adapter-cloudflare-workers
- title: Netlify, use_cases: deployment, netlify hosting, production builds, serverless functions, edge functions, static site hosting, path: kit/adapter-netlify
- title: Vercel, use_cases: deployment, vercel hosting, production builds, serverless functions, edge functions, isr, image optimization, environment variables, path: kit/adapter-vercel
- title: Writing adapters, use_cases: custom deployment, building adapters, unsupported platforms, adapter development, custom hosting environments, path: kit/writing-adapters
- title: Advanced routing, use_cases: advanced routing, dynamic routes, file viewers, nested paths, custom 404 pages, url validation, route parameters, multi-level navigation, path: kit/advanced-routing
- title: Hooks, use_cases: authentication, logging, error tracking, request interception, api proxying, custom routing, internationalization, database initialization, middleware logic, session management, path: kit/hooks
- title: Errors, use_cases: error handling, custom error pages, 404 pages, api error responses, production error logging, error tracking, type-safe errors, path: kit/errors
- title: Link options, use_cases: routing, navigation, multi-page apps, performance optimization, link preloading, forms with get method, search functionality, focus management, scroll behavior, path: kit/link-options
- title: Service workers, use_cases: offline support, pwa, caching strategies, performance optimization, precaching assets, network resilience, progressive web apps, path: kit/service-workers
- title: Server-only modules, use_cases: api keys, environment variables, sensitive data protection, backend security, preventing data leaks, server-side code isolation, path: kit/server-only-modules
- title: Snapshots, use_cases: forms, user input, preserving form data, multi-step forms, navigation state, preventing data loss, textarea content, input fields, comment systems, surveys, path: kit/snapshots
- title: Shallow routing, use_cases: modals, dialogs, image galleries, overlays, history-driven ui, mobile-friendly navigation, photo viewers, lightboxes, drawer menus, path: kit/shallow-routing
- title: Observability, use_cases: performance monitoring, debugging, observability, tracing requests, production diagnostics, analyzing slow requests, finding bottlenecks, monitoring server-side operations, path: kit/observability
- title: Packaging, use_cases: building component libraries, publishing npm packages, creating reusable svelte components, library development, package distribution, path: kit/packaging
- title: Auth, use_cases: authentication, login systems, user management, session handling, jwt tokens, protected routes, user credentials, authorization checks, path: kit/auth
- title: Performance, use_cases: performance optimization, slow loading pages, production deployment, debugging performance issues, reducing bundle size, improving load times, path: kit/performance
- title: Icons, use_cases: icons, ui components, styling, css frameworks, tailwind, unocss, performance optimization, dependency management, path: kit/icons
- title: Images, use_cases: image optimization, responsive images, performance, hero images, product photos, galleries, cms integration, cdn setup, asset management, path: kit/images
- title: Accessibility, use_cases: always, any sveltekit project, screen reader support, keyboard navigation, multi-page apps, client-side routing, internationalization, multilingual sites, path: kit/accessibility
- title: SEO, use_cases: seo optimization, search engine ranking, content sites, blogs, marketing sites, public-facing apps, sitemaps, amp pages, meta tags, performance optimization, path: kit/seo
- title: Frequently asked questions, use_cases: troubleshooting package imports, library compatibility issues, client-side code execution, external api integration, middleware setup, database configuration, view transitions, yarn configuration, path: kit/faq
- title: Integrations, use_cases: project setup, css preprocessors, postcss, scss, sass, less, stylus, typescript setup, adding integrations, tailwind, testing, auth, linting, formatting, path: kit/integrations
- title: Breakpoint Debugging, use_cases: debugging, breakpoints, development workflow, troubleshooting issues, vscode setup, ide configuration, inspecting code execution, path: kit/debugging
- title: Migrating to SvelteKit v2, use_cases: migration, upgrading from sveltekit 1 to 2, breaking changes, version updates, path: kit/migrating-to-sveltekit-2
- title: Migrating from Sapper, use_cases: migrating from sapper, upgrading legacy projects, sapper to sveltekit conversion, project modernization, path: kit/migrating
- title: Additional resources, use_cases: troubleshooting, getting help, finding examples, learning sveltekit, project templates, common issues, community support, path: kit/additional-resources
- title: Glossary, use_cases: rendering strategies, performance optimization, deployment configuration, seo requirements, static sites, spas, server-side rendering, prerendering, edge deployment, pwa development, path: kit/glossary
- title: @sveltejs/kit, use_cases: forms, form actions, server-side validation, form submission, error handling, redirects, json responses, http errors, server utilities, path: kit/@sveltejs-kit
- title: @sveltejs/kit/hooks, use_cases: middleware, request processing, authentication chains, logging, multiple hooks, request/response transformation, path: kit/@sveltejs-kit-hooks
- title: @sveltejs/kit/node/polyfills, use_cases: node.js environments, custom servers, non-standard runtimes, ssr setup, web api compatibility, polyfill requirements, path: kit/@sveltejs-kit-node-polyfills
- title: @sveltejs/kit/node, use_cases: node.js adapter, custom server setup, http integration, streaming files, node deployment, server-side rendering with node, path: kit/@sveltejs-kit-node
- title: @sveltejs/kit/vite, use_cases: project setup, vite configuration, initial sveltekit setup, build tooling, path: kit/@sveltejs-kit-vite
- title: $app/environment, use_cases: always, conditional logic, client-side code, server-side code, build-time logic, prerendering, development vs production, environment detection, path: kit/$app-environment
- title: $app/forms, use_cases: forms, user input, data submission, progressive enhancement, custom form handling, form validation, path: kit/$app-forms
- title: $app/navigation, use_cases: routing, navigation, multi-page apps, programmatic navigation, data reloading, preloading, shallow routing, navigation lifecycle, scroll handling, view transitions, path: kit/$app-navigation
- title: $app/paths, use_cases: static assets, images, fonts, public files, base path configuration, subdirectory deployment, cdn setup, asset urls, links, navigation, path: kit/$app-paths
- title: $app/server, use_cases: remote functions, server-side logic, data fetching, form handling, api endpoints, client-server communication, prerendering, file reading, batch queries, path: kit/$app-server
- title: $app/state, use_cases: routing, navigation, multi-page apps, loading states, url parameters, form handling, error states, version updates, page metadata, shallow routing, path: kit/$app-state
- title: $app/stores, use_cases: legacy projects, sveltekit pre-2.12, migration from stores to runes, maintaining older codebases, accessing page data, navigation state, app version updates, path: kit/$app-stores
- title: $app/types, use_cases: routing, navigation, type safety, route parameters, dynamic routes, link generation, pathname validation, multi-page apps, path: kit/$app-types
- title: $env/dynamic/private, use_cases: api keys, secrets management, server-side config, environment variables, backend logic, deployment-specific settings, private data handling, path: kit/$env-dynamic-private
- title: $env/dynamic/public, use_cases: environment variables, client-side config, runtime configuration, public api keys, deployment-specific settings, multi-environment apps, path: kit/$env-dynamic-public
- title: $env/static/private, use_cases: server-side api keys, backend secrets, database credentials, private configuration, build-time optimization, server endpoints, authentication tokens, path: kit/$env-static-private
- title: $env/static/public, use_cases: environment variables, public config, client-side data, api endpoints, build-time configuration, public constants, path: kit/$env-static-public
- title: $lib, use_cases: project setup, component organization, importing shared components, reusable ui elements, code structure, path: kit/$lib
- title: $service-worker, use_cases: offline support, pwa, service workers, caching strategies, progressive web apps, offline-first apps, path: kit/$service-worker
- title: Configuration, use_cases: project setup, configuration, adapters, deployment, build settings, environment variables, routing customization, prerendering, csp security, csrf protection, path configuration, typescript setup, path: kit/configuration
- title: Command Line Interface, use_cases: project setup, typescript configuration, generated types, ./$types imports, initial project configuration, path: kit/cli
- title: Types, use_cases: typescript, type safety, route parameters, api endpoints, load functions, form actions, generated types, jsconfig setup, path: kit/types
- title: Overview, use_cases: always, any svelte project, getting started, learning svelte, introduction, project setup, understanding framework basics, path: svelte/overview
- title: Getting started, use_cases: project setup, starting new svelte project, initial installation, choosing between sveltekit and vite, editor configuration, path: svelte/getting-started
- title: .svelte files, use_cases: always, any svelte project, component creation, project setup, learning svelte basics, path: svelte/svelte-files
- title: .svelte.js and .svelte.ts files, use_cases: shared reactive state, reusable reactive logic, state management across components, global stores, custom reactive utilities, path: svelte/svelte-js-files
- title: What are runes?, use_cases: always, any svelte 5 project, understanding core syntax, learning svelte 5, migration from svelte 4, path: svelte/what-are-runes
- title: $state, use_cases: always, any svelte project, core reactivity, state management, counters, forms, todo apps, interactive ui, data updates, class-based components, path: svelte/$state
- title: $derived, use_cases: always, any svelte project, computed values, reactive calculations, derived data, transforming state, dependent values, path: svelte/$derived
- title: $effect, use_cases: canvas drawing, third-party library integration, dom manipulation, side effects, intervals, timers, network requests, analytics tracking, path: svelte/$effect
- title: $props, use_cases: always, any svelte project, passing data to components, component communication, reusable components, component props, path: svelte/$props
- title: $bindable, use_cases: forms, user input, two-way data binding, custom input components, parent-child communication, reusable form fields, path: svelte/$bindable
- title: $inspect, use_cases: debugging, development, tracking state changes, reactive state monitoring, troubleshooting reactivity issues, path: svelte/$inspect
- title: $host, use_cases: custom elements, web components, dispatching custom events, component library, framework-agnostic components, path: svelte/$host
- title: Basic markup, use_cases: always, any svelte project, basic markup, html templating, component structure, attributes, events, props, text rendering, path: svelte/basic-markup
- title: {#if ...}, use_cases: always, conditional rendering, showing/hiding content, dynamic ui, user permissions, loading states, error handling, form validation, path: svelte/if
- title: {#each ...}, use_cases: always, lists, arrays, iteration, product listings, todos, tables, grids, dynamic content, shopping carts, user lists, comments, feeds, path: svelte/each
- title: {#key ...}, use_cases: animations, transitions, component reinitialization, forcing component remount, value-based ui updates, resetting component state, path: svelte/key
- title: {#await ...}, use_cases: async data fetching, api calls, loading states, promises, error handling, lazy loading components, dynamic imports, path: svelte/await
- title: {#snippet ...}, use_cases: reusable markup, component composition, passing content to components, table rows, list items, conditional rendering, reducing duplication, path: svelte/snippet
- title: {@render ...}, use_cases: reusable ui patterns, component composition, conditional rendering, fallback content, layout components, slot alternatives, template reuse, path: svelte/@render
- title: {@html ...}, use_cases: rendering html strings, cms content, rich text editors, markdown to html, blog posts, wysiwyg output, sanitized html injection, dynamic html content, path: svelte/@html
- title: {@attach ...}, use_cases: tooltips, popovers, dom manipulation, third-party libraries, canvas drawing, element lifecycle, interactive ui, custom directives, wrapper components, path: svelte/@attach
- title: {@const ...}, use_cases: computed values in loops, derived calculations in blocks, local variables in each iterations, complex list rendering, path: svelte/@const
- title: {@debug ...}, use_cases: debugging, development, troubleshooting, tracking state changes, monitoring variables, reactive data inspection, path: svelte/@debug
- title: bind:, use_cases: forms, user input, two-way data binding, interactive ui, media players, file uploads, checkboxes, radio buttons, select dropdowns, contenteditable, dimension tracking, path: svelte/bind
- title: use:, use_cases: custom directives, dom manipulation, third-party library integration, tooltips, click outside, gestures, focus management, element lifecycle hooks, path: svelte/use
- title: transition:, use_cases: animations, interactive ui, modals, dropdowns, notifications, conditional content, show/hide elements, smooth state changes, path: svelte/transition
- title: in: and out:, use_cases: animation, transitions, interactive ui, conditional rendering, independent enter/exit effects, modals, tooltips, notifications, path: svelte/in-and-out
- title: animate:, use_cases: sortable lists, drag and drop, reorderable items, todo lists, kanban boards, playlist editors, priority queues, animated list reordering, path: svelte/animate
- title: style:, use_cases: dynamic styling, conditional styles, theming, dark mode, responsive design, interactive ui, component styling, path: svelte/style
- title: class, use_cases: always, conditional styling, dynamic classes, tailwind css, component styling, reusable components, responsive design, path: svelte/class
- title: await, use_cases: async data fetching, loading states, server-side rendering, awaiting promises in components, async validation, concurrent data loading, path: svelte/await-expressions
- title: Scoped styles, use_cases: always, styling components, scoped css, component-specific styles, preventing style conflicts, animations, keyframes, path: svelte/scoped-styles
- title: Global styles, use_cases: global styles, third-party libraries, css resets, animations, styling body/html, overriding component styles, shared keyframes, base styles, path: svelte/global-styles
- title: Custom properties, use_cases: theming, custom styling, reusable components, design systems, dynamic colors, component libraries, ui customization, path: svelte/custom-properties
- title: Nested <style> elements, use_cases: component styling, scoped styles, dynamic styles, conditional styling, nested style tags, custom styling logic, path: svelte/nested-style-elements
- title: <svelte:boundary>, use_cases: error handling, async data loading, loading states, error recovery, flaky components, error reporting, resilient ui, path: svelte/svelte-boundary
- title: <svelte:window>, use_cases: keyboard shortcuts, scroll tracking, window resize handling, responsive layouts, online/offline detection, viewport dimensions, global event listeners, path: svelte/svelte-window
- title: <svelte:document>, use_cases: document events, visibility tracking, fullscreen detection, pointer lock, focus management, document-level interactions, path: svelte/svelte-document
- title: <svelte:body>, use_cases: mouse tracking, hover effects, cursor interactions, global body events, drag and drop, custom cursors, interactive backgrounds, body-level actions, path: svelte/svelte-body
- title: <svelte:head>, use_cases: seo optimization, page titles, meta tags, social media sharing, dynamic head content, multi-page apps, blog posts, product pages, path: svelte/svelte-head
- title: <svelte:element>, use_cases: dynamic content, cms integration, user-generated content, configurable ui, runtime element selection, flexible components, path: svelte/svelte-element
- title: <svelte:options>, use_cases: migration, custom elements, web components, legacy mode compatibility, runes mode setup, svg components, mathml components, css injection control, path: svelte/svelte-options
- title: Stores, use_cases: shared state, cross-component data, reactive values, async data streams, manual control over updates, rxjs integration, extracting logic, path: svelte/stores
- title: Context, use_cases: shared state, avoiding prop drilling, component communication, theme providers, user context, authentication state, configuration sharing, deeply nested components, path: svelte/context
- title: Lifecycle hooks, use_cases: component initialization, cleanup tasks, timers, subscriptions, dom measurements, chat windows, autoscroll features, migration from svelte 4, path: svelte/lifecycle-hooks
- title: Imperative component API, use_cases: project setup, client-side rendering, server-side rendering, ssr, hydration, testing, programmatic component creation, tooltips, dynamic mounting, path: svelte/imperative-component-api
- title: Hydratable data, use_cases: use title and path to estimate use case, path: svelte/hydratable
- title: Best practices, use_cases: use title and path to estimate use case, path: svelte/best-practices
- title: Testing, use_cases: testing, quality assurance, unit tests, integration tests, component tests, e2e tests, vitest setup, playwright setup, test automation, path: svelte/testing
- title: TypeScript, use_cases: typescript setup, type safety, component props typing, generic components, wrapper components, dom type augmentation, project configuration, path: svelte/typescript
- title: Custom elements, use_cases: web components, custom elements, component library, design system, framework-agnostic components, embedding svelte in non-svelte apps, shadow dom, path: svelte/custom-elements
- title: Svelte 4 migration guide, use_cases: upgrading svelte 3 to 4, version migration, updating dependencies, breaking changes, legacy project maintenance, path: svelte/v4-migration-guide
- title: Svelte 5 migration guide, use_cases: migrating from svelte 4 to 5, upgrading projects, learning svelte 5 syntax changes, runes migration, event handler updates, path: svelte/v5-migration-guide
- title: Frequently asked questions, use_cases: getting started, learning svelte, beginner setup, project initialization, vs code setup, formatting, testing, routing, mobile apps, troubleshooting, community support, path: svelte/faq
- title: svelte, use_cases: migration from svelte 4 to 5, upgrading legacy code, component lifecycle hooks, context api, mounting components, event dispatchers, typescript component types, path: svelte/svelte
- title: svelte/action, use_cases: typescript types, actions, use directive, dom manipulation, element lifecycle, custom behaviors, third-party library integration, path: svelte/svelte-action
- title: svelte/animate, use_cases: animated lists, sortable items, drag and drop, reordering elements, todo lists, kanban boards, playlist management, smooth position transitions, path: svelte/svelte-animate
- title: svelte/attachments, use_cases: library development, component libraries, programmatic element manipulation, migrating from actions to attachments, spreading props onto elements, path: svelte/svelte-attachments
- title: svelte/compiler, use_cases: build tools, custom compilers, ast manipulation, preprocessors, code transformation, migration scripts, syntax analysis, bundler plugins, dev tools, path: svelte/svelte-compiler
- title: svelte/easing, use_cases: animations, transitions, custom easing, smooth motion, interactive ui, modals, dropdowns, carousels, page transitions, scroll effects, path: svelte/svelte-easing
- title: svelte/events, use_cases: window events, document events, global event listeners, event delegation, programmatic event handling, cleanup functions, media queries, path: svelte/svelte-events
- title: svelte/legacy, use_cases: migration from svelte 4 to svelte 5, upgrading legacy code, event modifiers, class components, imperative component instantiation, path: svelte/svelte-legacy
- title: svelte/motion, use_cases: animation, smooth transitions, interactive ui, sliders, counters, physics-based motion, drag gestures, accessibility, reduced motion, path: svelte/svelte-motion
- title: svelte/reactivity/window, use_cases: responsive design, viewport tracking, scroll effects, window resize handling, online/offline detection, zoom level tracking, path: svelte/svelte-reactivity-window
- title: svelte/reactivity, use_cases: reactive data structures, state management with maps/sets, game boards, selection tracking, url manipulation, query params, real-time clocks, media queries, responsive design, path: svelte/svelte-reactivity
- title: svelte/server, use_cases: server-side rendering, ssr, static site generation, seo optimization, initial page load, pre-rendering, node.js server, custom server setup, path: svelte/svelte-server
- title: svelte/store, use_cases: state management, shared data, reactive stores, cross-component communication, global state, computed values, data synchronization, legacy svelte projects, path: svelte/svelte-store
- title: svelte/transition, use_cases: animations, transitions, interactive ui, modals, dropdowns, tooltips, notifications, svg animations, list animations, page transitions, path: svelte/svelte-transition
- title: Compiler errors, use_cases: animation, transitions, keyed each blocks, list animations, path: svelte/compiler-errors
- title: Compiler warnings, use_cases: accessibility, a11y compliance, wcag standards, screen readers, keyboard navigation, aria attributes, semantic html, interactive elements, path: svelte/compiler-warnings
- title: Runtime errors, use_cases: debugging errors, error handling, troubleshooting runtime issues, migration to svelte 5, component binding, effects and reactivity, path: svelte/runtime-errors
- title: Runtime warnings, use_cases: debugging state proxies, console logging reactive values, inspecting state changes, development troubleshooting, path: svelte/runtime-warnings
- title: Overview, use_cases: migrating from svelte 3/4 to svelte 5, maintaining legacy components, understanding deprecated features, gradual upgrade process, path: svelte/legacy-overview
- title: Reactive let/var declarations, use_cases: migration, legacy svelte projects, upgrading from svelte 4, understanding old reactivity, maintaining existing code, learning runes differences, path: svelte/legacy-let
- title: Reactive $: statements, use_cases: legacy mode, migration from svelte 4, reactive statements, computed values, derived state, side effects, path: svelte/legacy-reactive-assignments
- title: export let, use_cases: legacy mode, migration from svelte 4, maintaining older projects, component props without runes, exporting component methods, renaming reserved word props, path: svelte/legacy-export-let
- title: $$props and $$restProps, use_cases: legacy mode migration, component wrappers, prop forwarding, button components, reusable ui components, spreading props to child elements, path: svelte/legacy-$$props-and-$$restProps
- title: on:, use_cases: legacy mode, event handling, button clicks, forms, user interactions, component communication, event forwarding, event modifiers, path: svelte/legacy-on
- title: <slot>, use_cases: legacy mode, migrating from svelte 4, component composition, reusable components, passing content to components, modals, layouts, wrappers, path: svelte/legacy-slots
- title: $$slots, use_cases: legacy mode, conditional slot rendering, optional content sections, checking if slots provided, migrating from legacy to runes, path: svelte/legacy-$$slots
- title: <svelte:fragment>, use_cases: named slots, component composition, layout systems, avoiding wrapper divs, legacy svelte projects, slot content organization, path: svelte/legacy-svelte-fragment
- title: <svelte:component>, use_cases: dynamic components, component switching, conditional rendering, legacy mode migration, tabbed interfaces, multi-step forms, path: svelte/legacy-svelte-component
- title: <svelte:self>, use_cases: recursive components, tree structures, nested menus, file explorers, comment threads, hierarchical data, path: svelte/legacy-svelte-self
- title: Imperative component API, use_cases: migration from svelte 3/4 to 5, legacy component api, maintaining old projects, understanding deprecated patterns, path: svelte/legacy-component-api
</available-docs>
These are the available documentation sections that `list-sections` will return, you do not need to call it again.
Every time you write a Svelte component or a Svelte module you MUST invoke the `svelte-autofixer` tool providing the code. The tool will return a list of issues or suggestions. If there are any issues or suggestions you MUST fix them and call the tool again with the updated code. You MUST keep doing this until the tool returns no issues or suggestions. Only then you can return the code to the user.
This is the task you will work on:
<task>
[YOUR TASK HERE]
</task>
If you are not writing the code into a file, once you have the final version of the code ask the user if it wants to generate a playground link to quickly check the code in it and if it answer yes call the `playground-link` tool and return the url to the user nicely formatted. The playground link MUST be generated only once you have the final version of the code and you are ready to share it, it MUST include an entry point file called `App.svelte` where the main component should live. If you have multiple files to include in the playground link you can include them all at the root.
````
<!-- prettier-ignore-end -->
</details>

View File

@@ -4,206 +4,4 @@ title: Prompts
This is the list of available prompts provided by the MCP server. Prompts are selected by the user and are sent as a user message. They can be useful to write repetitive instructions for the LLM on how to properly use the MCP server.
## svelte-task
This prompt should be used whenever you are asking the model to work on a Svelte-related task. It will instruct the LLM which documentation sections are available, which tools to invoke, when to invoke them, and how to interpret the results.
<details>
<summary>Copy the prompt</summary>
```md
You are a Svelte expert tasked to build components and utilities for Svelte developers. If you need documentation for anything related to Svelte you can invoke the tool `get-documentation` with one of the following paths. However: before invoking the `get-documentation` tool, try to answer the users query using your own knowledge and the `svelte-autofixer` tool. Be mindful of how many section you request, since it is token-intensive!
<available-docs>
- title: Overview, use_cases: project setup, creating new svelte apps, scaffolding, cli tools, initializing projects, path: cli/overview
- title: Frequently asked questions, use_cases: project setup, initializing new svelte projects, troubleshooting cli installation, package manager configuration, path: cli/faq
- title: sv create, use_cases: project setup, starting new sveltekit app, initializing project, creating from playground, choosing project template, path: cli/sv-create
- title: sv add, use_cases: project setup, adding features to existing projects, integrating tools, testing setup, styling setup, authentication, database setup, deployment adapters, path: cli/sv-add
- title: sv check, use_cases: code quality, ci/cd pipelines, error checking, typescript projects, pre-commit hooks, finding unused css, accessibility auditing, production builds, path: cli/sv-check
- title: sv migrate, use_cases: migration, upgrading svelte versions, upgrading sveltekit versions, modernizing codebase, svelte 3 to 4, svelte 4 to 5, sveltekit 1 to 2, adopting runes, refactoring deprecated apis, path: cli/sv-migrate
- title: devtools-json, use_cases: development setup, chrome devtools integration, browser-based editing, local development workflow, debugging setup, path: cli/devtools-json
- title: drizzle, use_cases: database setup, sql queries, orm integration, data modeling, postgresql, mysql, sqlite, server-side data access, database migrations, type-safe queries, path: cli/drizzle
- title: eslint, use_cases: code quality, linting, error detection, project setup, code standards, team collaboration, typescript projects, path: cli/eslint
- title: lucia, use_cases: authentication, login systems, user management, registration pages, session handling, auth setup, path: cli/lucia
- title: mcp, use_cases: use title and path to estimate use case, path: cli/mcp
- title: mdsvex, use_cases: blog, content sites, markdown rendering, documentation sites, technical writing, cms integration, article pages, path: cli/mdsvex
- title: paraglide, use_cases: internationalization, multi-language sites, i18n, translation, localization, language switching, global apps, multilingual content, path: cli/paraglide
- title: playwright, use_cases: browser testing, e2e testing, integration testing, test automation, quality assurance, ci/cd pipelines, testing user flows, path: cli/playwright
- title: prettier, use_cases: code formatting, project setup, code style consistency, team collaboration, linting configuration, path: cli/prettier
- title: storybook, use_cases: component development, design systems, ui library, isolated component testing, documentation, visual testing, component showcase, path: cli/storybook
- title: sveltekit-adapter, use_cases: deployment, production builds, hosting setup, choosing deployment platform, configuring adapters, static site generation, node server, vercel, cloudflare, netlify, path: cli/sveltekit-adapter
- title: tailwindcss, use_cases: project setup, styling, css framework, rapid prototyping, utility-first css, design systems, responsive design, adding tailwind to svelte, path: cli/tailwind
- title: vitest, use_cases: testing, unit tests, component testing, test setup, quality assurance, ci/cd pipelines, test-driven development, path: cli/vitest
- title: Introduction, use_cases: learning sveltekit, project setup, understanding framework basics, choosing between svelte and sveltekit, getting started with full-stack apps, path: kit/introduction
- title: Creating a project, use_cases: project setup, starting new sveltekit app, initial development environment, first-time sveltekit users, scaffolding projects, path: kit/creating-a-project
- title: Project types, use_cases: deployment, project setup, choosing adapters, ssg, spa, ssr, serverless, mobile apps, desktop apps, pwa, offline apps, browser extensions, separate backend, docker containers, path: kit/project-types
- title: Project structure, use_cases: project setup, understanding file structure, organizing code, starting new project, learning sveltekit basics, path: kit/project-structure
- title: Web standards, use_cases: always, any sveltekit project, data fetching, forms, api routes, server-side rendering, deployment to various platforms, path: kit/web-standards
- title: Routing, use_cases: routing, navigation, multi-page apps, project setup, file structure, api endpoints, data loading, layouts, error pages, always, path: kit/routing
- title: Loading data, use_cases: data fetching, api calls, database queries, dynamic routes, page initialization, loading states, authentication checks, ssr data, form data, content rendering, path: kit/load
- title: Form actions, use_cases: forms, user input, data submission, authentication, login systems, user registration, progressive enhancement, validation errors, path: kit/form-actions
- title: Page options, use_cases: prerendering static sites, ssr configuration, spa setup, client-side rendering control, url trailing slash handling, adapter deployment config, build optimization, path: kit/page-options
- title: State management, use_cases: sveltekit, server-side rendering, ssr, state management, authentication, data persistence, load functions, context api, navigation, component lifecycle, path: kit/state-management
- title: Remote functions, use_cases: data fetching, server-side logic, database queries, type-safe client-server communication, forms, user input, mutations, authentication, crud operations, optimistic updates, path: kit/remote-functions
- title: Building your app, use_cases: production builds, deployment preparation, build process optimization, adapter configuration, preview before deployment, path: kit/building-your-app
- title: Adapters, use_cases: deployment, production builds, hosting setup, choosing deployment platform, configuring adapters, path: kit/adapters
- title: Zero-config deployments, use_cases: deployment, production builds, hosting setup, choosing deployment platform, ci/cd configuration, path: kit/adapter-auto
- title: Node servers, use_cases: deployment, production builds, node.js hosting, custom server setup, environment configuration, reverse proxy setup, docker deployment, systemd services, path: kit/adapter-node
- title: Static site generation, use_cases: static site generation, ssg, prerendering, deployment, github pages, spa mode, blogs, documentation sites, marketing sites, path: kit/adapter-static
- title: Single-page apps, use_cases: spa mode, single-page apps, client-only rendering, static hosting, mobile app wrappers, no server-side logic, adapter-static setup, fallback pages, path: kit/single-page-apps
- title: Cloudflare, use_cases: deployment, cloudflare workers, cloudflare pages, hosting setup, production builds, serverless deployment, edge computing, path: kit/adapter-cloudflare
- title: Cloudflare Workers, use_cases: deploying to cloudflare workers, cloudflare workers sites deployment, legacy cloudflare adapter, wrangler configuration, cloudflare platform bindings, path: kit/adapter-cloudflare-workers
- title: Netlify, use_cases: deployment, netlify hosting, production builds, serverless functions, edge functions, static site hosting, path: kit/adapter-netlify
- title: Vercel, use_cases: deployment, vercel hosting, production builds, serverless functions, edge functions, isr, image optimization, environment variables, path: kit/adapter-vercel
- title: Writing adapters, use_cases: custom deployment, building adapters, unsupported platforms, adapter development, custom hosting environments, path: kit/writing-adapters
- title: Advanced routing, use_cases: advanced routing, dynamic routes, file viewers, nested paths, custom 404 pages, url validation, route parameters, multi-level navigation, path: kit/advanced-routing
- title: Hooks, use_cases: authentication, logging, error tracking, request interception, api proxying, custom routing, internationalization, database initialization, middleware logic, session management, path: kit/hooks
- title: Errors, use_cases: error handling, custom error pages, 404 pages, api error responses, production error logging, error tracking, type-safe errors, path: kit/errors
- title: Link options, use_cases: routing, navigation, multi-page apps, performance optimization, link preloading, forms with get method, search functionality, focus management, scroll behavior, path: kit/link-options
- title: Service workers, use_cases: offline support, pwa, caching strategies, performance optimization, precaching assets, network resilience, progressive web apps, path: kit/service-workers
- title: Server-only modules, use_cases: api keys, environment variables, sensitive data protection, backend security, preventing data leaks, server-side code isolation, path: kit/server-only-modules
- title: Snapshots, use_cases: forms, user input, preserving form data, multi-step forms, navigation state, preventing data loss, textarea content, input fields, comment systems, surveys, path: kit/snapshots
- title: Shallow routing, use_cases: modals, dialogs, image galleries, overlays, history-driven ui, mobile-friendly navigation, photo viewers, lightboxes, drawer menus, path: kit/shallow-routing
- title: Observability, use_cases: performance monitoring, debugging, observability, tracing requests, production diagnostics, analyzing slow requests, finding bottlenecks, monitoring server-side operations, path: kit/observability
- title: Packaging, use_cases: building component libraries, publishing npm packages, creating reusable svelte components, library development, package distribution, path: kit/packaging
- title: Auth, use_cases: authentication, login systems, user management, session handling, jwt tokens, protected routes, user credentials, authorization checks, path: kit/auth
- title: Performance, use_cases: performance optimization, slow loading pages, production deployment, debugging performance issues, reducing bundle size, improving load times, path: kit/performance
- title: Icons, use_cases: icons, ui components, styling, css frameworks, tailwind, unocss, performance optimization, dependency management, path: kit/icons
- title: Images, use_cases: image optimization, responsive images, performance, hero images, product photos, galleries, cms integration, cdn setup, asset management, path: kit/images
- title: Accessibility, use_cases: always, any sveltekit project, screen reader support, keyboard navigation, multi-page apps, client-side routing, internationalization, multilingual sites, path: kit/accessibility
- title: SEO, use_cases: seo optimization, search engine ranking, content sites, blogs, marketing sites, public-facing apps, sitemaps, amp pages, meta tags, performance optimization, path: kit/seo
- title: Frequently asked questions, use_cases: troubleshooting package imports, library compatibility issues, client-side code execution, external api integration, middleware setup, database configuration, view transitions, yarn configuration, path: kit/faq
- title: Integrations, use_cases: project setup, css preprocessors, postcss, scss, sass, less, stylus, typescript setup, adding integrations, tailwind, testing, auth, linting, formatting, path: kit/integrations
- title: Breakpoint Debugging, use_cases: debugging, breakpoints, development workflow, troubleshooting issues, vscode setup, ide configuration, inspecting code execution, path: kit/debugging
- title: Migrating to SvelteKit v2, use_cases: migration, upgrading from sveltekit 1 to 2, breaking changes, version updates, path: kit/migrating-to-sveltekit-2
- title: Migrating from Sapper, use_cases: migrating from sapper, upgrading legacy projects, sapper to sveltekit conversion, project modernization, path: kit/migrating
- title: Additional resources, use_cases: troubleshooting, getting help, finding examples, learning sveltekit, project templates, common issues, community support, path: kit/additional-resources
- title: Glossary, use_cases: rendering strategies, performance optimization, deployment configuration, seo requirements, static sites, spas, server-side rendering, prerendering, edge deployment, pwa development, path: kit/glossary
- title: @sveltejs/kit, use_cases: forms, form actions, server-side validation, form submission, error handling, redirects, json responses, http errors, server utilities, path: kit/@sveltejs-kit
- title: @sveltejs/kit/hooks, use_cases: middleware, request processing, authentication chains, logging, multiple hooks, request/response transformation, path: kit/@sveltejs-kit-hooks
- title: @sveltejs/kit/node/polyfills, use_cases: node.js environments, custom servers, non-standard runtimes, ssr setup, web api compatibility, polyfill requirements, path: kit/@sveltejs-kit-node-polyfills
- title: @sveltejs/kit/node, use_cases: node.js adapter, custom server setup, http integration, streaming files, node deployment, server-side rendering with node, path: kit/@sveltejs-kit-node
- title: @sveltejs/kit/vite, use_cases: project setup, vite configuration, initial sveltekit setup, build tooling, path: kit/@sveltejs-kit-vite
- title: $app/environment, use_cases: always, conditional logic, client-side code, server-side code, build-time logic, prerendering, development vs production, environment detection, path: kit/$app-environment
- title: $app/forms, use_cases: forms, user input, data submission, progressive enhancement, custom form handling, form validation, path: kit/$app-forms
- title: $app/navigation, use_cases: routing, navigation, multi-page apps, programmatic navigation, data reloading, preloading, shallow routing, navigation lifecycle, scroll handling, view transitions, path: kit/$app-navigation
- title: $app/paths, use_cases: static assets, images, fonts, public files, base path configuration, subdirectory deployment, cdn setup, asset urls, links, navigation, path: kit/$app-paths
- title: $app/server, use_cases: remote functions, server-side logic, data fetching, form handling, api endpoints, client-server communication, prerendering, file reading, batch queries, path: kit/$app-server
- title: $app/state, use_cases: routing, navigation, multi-page apps, loading states, url parameters, form handling, error states, version updates, page metadata, shallow routing, path: kit/$app-state
- title: $app/stores, use_cases: legacy projects, sveltekit pre-2.12, migration from stores to runes, maintaining older codebases, accessing page data, navigation state, app version updates, path: kit/$app-stores
- title: $app/types, use_cases: routing, navigation, type safety, route parameters, dynamic routes, link generation, pathname validation, multi-page apps, path: kit/$app-types
- title: $env/dynamic/private, use_cases: api keys, secrets management, server-side config, environment variables, backend logic, deployment-specific settings, private data handling, path: kit/$env-dynamic-private
- title: $env/dynamic/public, use_cases: environment variables, client-side config, runtime configuration, public api keys, deployment-specific settings, multi-environment apps, path: kit/$env-dynamic-public
- title: $env/static/private, use_cases: server-side api keys, backend secrets, database credentials, private configuration, build-time optimization, server endpoints, authentication tokens, path: kit/$env-static-private
- title: $env/static/public, use_cases: environment variables, public config, client-side data, api endpoints, build-time configuration, public constants, path: kit/$env-static-public
- title: $lib, use_cases: project setup, component organization, importing shared components, reusable ui elements, code structure, path: kit/$lib
- title: $service-worker, use_cases: offline support, pwa, service workers, caching strategies, progressive web apps, offline-first apps, path: kit/$service-worker
- title: Configuration, use_cases: project setup, configuration, adapters, deployment, build settings, environment variables, routing customization, prerendering, csp security, csrf protection, path configuration, typescript setup, path: kit/configuration
- title: Command Line Interface, use_cases: project setup, typescript configuration, generated types, ./$types imports, initial project configuration, path: kit/cli
- title: Types, use_cases: typescript, type safety, route parameters, api endpoints, load functions, form actions, generated types, jsconfig setup, path: kit/types
- title: Overview, use_cases: use title and path to estimate use case, path: mcp/overview
- title: Local setup, use_cases: use title and path to estimate use case, path: mcp/local-setup
- title: Remote setup, use_cases: use title and path to estimate use case, path: mcp/remote-setup
- title: Tools, use_cases: use title and path to estimate use case, path: mcp/tools
- title: Resources, use_cases: use title and path to estimate use case, path: mcp/resources
- title: Prompts, use_cases: use title and path to estimate use case, path: mcp/prompts
- title: Overview, use_cases: always, any svelte project, getting started, learning svelte, introduction, project setup, understanding framework basics, path: svelte/overview
- title: Getting started, use_cases: project setup, starting new svelte project, initial installation, choosing between sveltekit and vite, editor configuration, path: svelte/getting-started
- title: .svelte files, use_cases: always, any svelte project, component creation, project setup, learning svelte basics, path: svelte/svelte-files
- title: .svelte.js and .svelte.ts files, use_cases: shared reactive state, reusable reactive logic, state management across components, global stores, custom reactive utilities, path: svelte/svelte-js-files
- title: What are runes?, use_cases: always, any svelte 5 project, understanding core syntax, learning svelte 5, migration from svelte 4, path: svelte/what-are-runes
- title: $state, use_cases: always, any svelte project, core reactivity, state management, counters, forms, todo apps, interactive ui, data updates, class-based components, path: svelte/$state
- title: $derived, use_cases: always, any svelte project, computed values, reactive calculations, derived data, transforming state, dependent values, path: svelte/$derived
- title: $effect, use_cases: canvas drawing, third-party library integration, dom manipulation, side effects, intervals, timers, network requests, analytics tracking, path: svelte/$effect
- title: $props, use_cases: always, any svelte project, passing data to components, component communication, reusable components, component props, path: svelte/$props
- title: $bindable, use_cases: forms, user input, two-way data binding, custom input components, parent-child communication, reusable form fields, path: svelte/$bindable
- title: $inspect, use_cases: debugging, development, tracking state changes, reactive state monitoring, troubleshooting reactivity issues, path: svelte/$inspect
- title: $host, use_cases: custom elements, web components, dispatching custom events, component library, framework-agnostic components, path: svelte/$host
- title: Basic markup, use_cases: always, any svelte project, basic markup, html templating, component structure, attributes, events, props, text rendering, path: svelte/basic-markup
- title: {#if ...}, use_cases: always, conditional rendering, showing/hiding content, dynamic ui, user permissions, loading states, error handling, form validation, path: svelte/if
- title: {#each ...}, use_cases: always, lists, arrays, iteration, product listings, todos, tables, grids, dynamic content, shopping carts, user lists, comments, feeds, path: svelte/each
- title: {#key ...}, use_cases: animations, transitions, component reinitialization, forcing component remount, value-based ui updates, resetting component state, path: svelte/key
- title: {#await ...}, use_cases: async data fetching, api calls, loading states, promises, error handling, lazy loading components, dynamic imports, path: svelte/await
- title: {#snippet ...}, use_cases: reusable markup, component composition, passing content to components, table rows, list items, conditional rendering, reducing duplication, path: svelte/snippet
- title: {@render ...}, use_cases: reusable ui patterns, component composition, conditional rendering, fallback content, layout components, slot alternatives, template reuse, path: svelte/@render
- title: {@html ...}, use_cases: rendering html strings, cms content, rich text editors, markdown to html, blog posts, wysiwyg output, sanitized html injection, dynamic html content, path: svelte/@html
- title: {@attach ...}, use_cases: tooltips, popovers, dom manipulation, third-party libraries, canvas drawing, element lifecycle, interactive ui, custom directives, wrapper components, path: svelte/@attach
- title: {@const ...}, use_cases: computed values in loops, derived calculations in blocks, local variables in each iterations, complex list rendering, path: svelte/@const
- title: {@debug ...}, use_cases: debugging, development, troubleshooting, tracking state changes, monitoring variables, reactive data inspection, path: svelte/@debug
- title: bind:, use_cases: forms, user input, two-way data binding, interactive ui, media players, file uploads, checkboxes, radio buttons, select dropdowns, contenteditable, dimension tracking, path: svelte/bind
- title: use:, use_cases: custom directives, dom manipulation, third-party library integration, tooltips, click outside, gestures, focus management, element lifecycle hooks, path: svelte/use
- title: transition:, use_cases: animations, interactive ui, modals, dropdowns, notifications, conditional content, show/hide elements, smooth state changes, path: svelte/transition
- title: in: and out:, use_cases: animation, transitions, interactive ui, conditional rendering, independent enter/exit effects, modals, tooltips, notifications, path: svelte/in-and-out
- title: animate:, use_cases: sortable lists, drag and drop, reorderable items, todo lists, kanban boards, playlist editors, priority queues, animated list reordering, path: svelte/animate
- title: style:, use_cases: dynamic styling, conditional styles, theming, dark mode, responsive design, interactive ui, component styling, path: svelte/style
- title: class, use_cases: always, conditional styling, dynamic classes, tailwind css, component styling, reusable components, responsive design, path: svelte/class
- title: await, use_cases: async data fetching, loading states, server-side rendering, awaiting promises in components, async validation, concurrent data loading, path: svelte/await-expressions
- title: Scoped styles, use_cases: always, styling components, scoped css, component-specific styles, preventing style conflicts, animations, keyframes, path: svelte/scoped-styles
- title: Global styles, use_cases: global styles, third-party libraries, css resets, animations, styling body/html, overriding component styles, shared keyframes, base styles, path: svelte/global-styles
- title: Custom properties, use_cases: theming, custom styling, reusable components, design systems, dynamic colors, component libraries, ui customization, path: svelte/custom-properties
- title: Nested <style> elements, use_cases: component styling, scoped styles, dynamic styles, conditional styling, nested style tags, custom styling logic, path: svelte/nested-style-elements
- title: <svelte:boundary>, use_cases: error handling, async data loading, loading states, error recovery, flaky components, error reporting, resilient ui, path: svelte/svelte-boundary
- title: <svelte:window>, use_cases: keyboard shortcuts, scroll tracking, window resize handling, responsive layouts, online/offline detection, viewport dimensions, global event listeners, path: svelte/svelte-window
- title: <svelte:document>, use_cases: document events, visibility tracking, fullscreen detection, pointer lock, focus management, document-level interactions, path: svelte/svelte-document
- title: <svelte:body>, use_cases: mouse tracking, hover effects, cursor interactions, global body events, drag and drop, custom cursors, interactive backgrounds, body-level actions, path: svelte/svelte-body
- title: <svelte:head>, use_cases: seo optimization, page titles, meta tags, social media sharing, dynamic head content, multi-page apps, blog posts, product pages, path: svelte/svelte-head
- title: <svelte:element>, use_cases: dynamic content, cms integration, user-generated content, configurable ui, runtime element selection, flexible components, path: svelte/svelte-element
- title: <svelte:options>, use_cases: migration, custom elements, web components, legacy mode compatibility, runes mode setup, svg components, mathml components, css injection control, path: svelte/svelte-options
- title: Stores, use_cases: shared state, cross-component data, reactive values, async data streams, manual control over updates, rxjs integration, extracting logic, path: svelte/stores
- title: Context, use_cases: shared state, avoiding prop drilling, component communication, theme providers, user context, authentication state, configuration sharing, deeply nested components, path: svelte/context
- title: Lifecycle hooks, use_cases: component initialization, cleanup tasks, timers, subscriptions, dom measurements, chat windows, autoscroll features, migration from svelte 4, path: svelte/lifecycle-hooks
- title: Imperative component API, use_cases: project setup, client-side rendering, server-side rendering, ssr, hydration, testing, programmatic component creation, tooltips, dynamic mounting, path: svelte/imperative-component-api
- title: Hydratable data, use_cases: use title and path to estimate use case, path: svelte/hydratable
- title: Testing, use_cases: testing, quality assurance, unit tests, integration tests, component tests, e2e tests, vitest setup, playwright setup, test automation, path: svelte/testing
- title: TypeScript, use_cases: typescript setup, type safety, component props typing, generic components, wrapper components, dom type augmentation, project configuration, path: svelte/typescript
- title: Custom elements, use_cases: web components, custom elements, component library, design system, framework-agnostic components, embedding svelte in non-svelte apps, shadow dom, path: svelte/custom-elements
- title: Svelte 4 migration guide, use_cases: upgrading svelte 3 to 4, version migration, updating dependencies, breaking changes, legacy project maintenance, path: svelte/v4-migration-guide
- title: Svelte 5 migration guide, use_cases: migrating from svelte 4 to 5, upgrading projects, learning svelte 5 syntax changes, runes migration, event handler updates, path: svelte/v5-migration-guide
- title: Frequently asked questions, use_cases: getting started, learning svelte, beginner setup, project initialization, vs code setup, formatting, testing, routing, mobile apps, troubleshooting, community support, path: svelte/faq
- title: svelte, use_cases: migration from svelte 4 to 5, upgrading legacy code, component lifecycle hooks, context api, mounting components, event dispatchers, typescript component types, path: svelte/svelte
- title: svelte/action, use_cases: typescript types, actions, use directive, dom manipulation, element lifecycle, custom behaviors, third-party library integration, path: svelte/svelte-action
- title: svelte/animate, use_cases: animated lists, sortable items, drag and drop, reordering elements, todo lists, kanban boards, playlist management, smooth position transitions, path: svelte/svelte-animate
- title: svelte/attachments, use_cases: library development, component libraries, programmatic element manipulation, migrating from actions to attachments, spreading props onto elements, path: svelte/svelte-attachments
- title: svelte/compiler, use_cases: build tools, custom compilers, ast manipulation, preprocessors, code transformation, migration scripts, syntax analysis, bundler plugins, dev tools, path: svelte/svelte-compiler
- title: svelte/easing, use_cases: animations, transitions, custom easing, smooth motion, interactive ui, modals, dropdowns, carousels, page transitions, scroll effects, path: svelte/svelte-easing
- title: svelte/events, use_cases: window events, document events, global event listeners, event delegation, programmatic event handling, cleanup functions, media queries, path: svelte/svelte-events
- title: svelte/legacy, use_cases: migration from svelte 4 to svelte 5, upgrading legacy code, event modifiers, class components, imperative component instantiation, path: svelte/svelte-legacy
- title: svelte/motion, use_cases: animation, smooth transitions, interactive ui, sliders, counters, physics-based motion, drag gestures, accessibility, reduced motion, path: svelte/svelte-motion
- title: svelte/reactivity/window, use_cases: responsive design, viewport tracking, scroll effects, window resize handling, online/offline detection, zoom level tracking, path: svelte/svelte-reactivity-window
- title: svelte/reactivity, use_cases: reactive data structures, state management with maps/sets, game boards, selection tracking, url manipulation, query params, real-time clocks, media queries, responsive design, path: svelte/svelte-reactivity
- title: svelte/server, use_cases: server-side rendering, ssr, static site generation, seo optimization, initial page load, pre-rendering, node.js server, custom server setup, path: svelte/svelte-server
- title: svelte/store, use_cases: state management, shared data, reactive stores, cross-component communication, global state, computed values, data synchronization, legacy svelte projects, path: svelte/svelte-store
- title: svelte/transition, use_cases: animations, transitions, interactive ui, modals, dropdowns, tooltips, notifications, svg animations, list animations, page transitions, path: svelte/svelte-transition
- title: Compiler errors, use_cases: animation, transitions, keyed each blocks, list animations, path: svelte/compiler-errors
- title: Compiler warnings, use_cases: accessibility, a11y compliance, wcag standards, screen readers, keyboard navigation, aria attributes, semantic html, interactive elements, path: svelte/compiler-warnings
- title: Runtime errors, use_cases: debugging errors, error handling, troubleshooting runtime issues, migration to svelte 5, component binding, effects and reactivity, path: svelte/runtime-errors
- title: Runtime warnings, use_cases: debugging state proxies, console logging reactive values, inspecting state changes, development troubleshooting, path: svelte/runtime-warnings
- title: Overview, use_cases: migrating from svelte 3/4 to svelte 5, maintaining legacy components, understanding deprecated features, gradual upgrade process, path: svelte/legacy-overview
- title: Reactive let/var declarations, use_cases: migration, legacy svelte projects, upgrading from svelte 4, understanding old reactivity, maintaining existing code, learning runes differences, path: svelte/legacy-let
- title: Reactive $: statements, use_cases: legacy mode, migration from svelte 4, reactive statements, computed values, derived state, side effects, path: svelte/legacy-reactive-assignments
- title: export let, use_cases: legacy mode, migration from svelte 4, maintaining older projects, component props without runes, exporting component methods, renaming reserved word props, path: svelte/legacy-export-let
- title: $$props and $$restProps, use_cases: legacy mode migration, component wrappers, prop forwarding, button components, reusable ui components, spreading props to child elements, path: svelte/legacy-$$props-and-$$restProps
- title: on:, use_cases: legacy mode, event handling, button clicks, forms, user interactions, component communication, event forwarding, event modifiers, path: svelte/legacy-on
- title: <slot>, use_cases: legacy mode, migrating from svelte 4, component composition, reusable components, passing content to components, modals, layouts, wrappers, path: svelte/legacy-slots
- title: $$slots, use_cases: legacy mode, conditional slot rendering, optional content sections, checking if slots provided, migrating from legacy to runes, path: svelte/legacy-$$slots
- title: <svelte:fragment>, use_cases: named slots, component composition, layout systems, avoiding wrapper divs, legacy svelte projects, slot content organization, path: svelte/legacy-svelte-fragment
- title: <svelte:component>, use_cases: dynamic components, component switching, conditional rendering, legacy mode migration, tabbed interfaces, multi-step forms, path: svelte/legacy-svelte-component
- title: <svelte:self>, use_cases: recursive components, tree structures, nested menus, file explorers, comment threads, hierarchical data, path: svelte/legacy-svelte-self
- title: Imperative component API, use_cases: migration from svelte 3/4 to 5, legacy component api, maintaining old projects, understanding deprecated patterns, path: svelte/legacy-component-api
</available-docs>
These are the available documentation sections that `list-sections` will return, you do not need to call it again.
Every time you write a Svelte component or a Svelte module you MUST invoke the `svelte-autofixer` tool providing the code. The tool will return a list of issues or suggestions. If there are any issues or suggestions you MUST fix them and call the tool again with the updated code. You MUST keep doing this until the tool returns no issues or suggestions. Only then you can return the code to the user.
This is the task you will work on:
<task>
[YOUR TASK HERE]
</task>
If you are not writing the code into a file, once you have the final version of the code ask the user if it wants to generate a playground link to quickly check the code in it and if it answer yes call the `playground-link` tool and return the url to the user nicely formatted. The playground link MUST be generated only once you have the final version of the code and you are ready to share it, it MUST include an entry point file called `App.svelte` where the main component should live. If you have multiple files to include in the playground link you can include them all at the root.
```
</details>
@include .generated/prompts.md

View File

@@ -2,7 +2,7 @@
title: Overview
---
The open source [repository](https://github.com/sveltejs/mcp) containing the code for the MCP server is also a Claude Code Marketplace plugin.
The open source [repository](https://github.com/sveltejs/ai-tools) containing the code for the MCP server is also a Claude Code Marketplace plugin.
The marketplace allows you to install the `svelte` plugin which will give you the remote MCP server, [skills](skills) to instruct the LLM on how to properly write Svelte 5 code, and a specialized agent for editing Svelte files.
@@ -13,7 +13,7 @@ If possible, we recommend that you instruct the LLM to execute MCP calls with th
To add the repository as a marketplace, launch Claude Code and type the following:
```bash
/plugin marketplace add sveltejs/mcp
/plugin marketplace add sveltejs/ai-tools
```
Then, install the Svelte plugin:

View File

@@ -6,7 +6,7 @@ OpenCode has a [plugin system](https://opencode.ai/docs/plugins/) that allows de
## Installation
To install the plugin in OpenCode you can edit your [OpenCode config]() (either the global or the local one), adding `@sveltejs/opencode` to the list of plugins.
To install the plugin in OpenCode you can edit your [OpenCode config](https://opencode.ai/docs/config/) (either the global or the local one), adding `@sveltejs/opencode` to the list of plugins.
```json
{
@@ -23,20 +23,31 @@ The default configuration for the Svelte OpenCode plugin looks like this...
```json
{
"$schema": "https://raw.githubusercontent.com/sveltejs/mcp/refs/heads/main/packages/opencode/schema.json",
"$schema": "https://svelte.dev/opencode/schema.json",
"mcp": {
"type": "remote",
"enabled": true
},
"subagent": {
"enabled": true
"enabled": true,
"agents": {
"svelte-file-editor": {
"model": "other-model", // defaults to the same as main agent,
"temperature": 1, // default to unset
"top_p": 0.7, // default to unset,
"maxSteps": 20 // default to unlimited
}
}
},
"skills": {
"enabled": true // it can also be an array of all the skills to enable like ['svelte-core-bestpractices']
},
"instructions": {
"enabled": true
}
}
```
...but if you prefer, you can enable only the subagent, only the MCP, only the skills, or configure the kind of MCP server you want to use (`local` or `remote`).
...but if you prefer, you can enable only the subagent, only the MCP, only the skills (`enabled` supports both a boolean or an array containing the name of all the skills to enable), or configure the kind of MCP server you want to use (`local` or `remote`).
You can place this file in `~/.config/opencode/svelte.json` or, if you have an `OPENCODE_CONFIG_DIR` environment variable specified, at `$OPENCODE_CONFIG_DIR/svelte.json`.
You can place this file in `./.opencode/svelte.json` (in your project), in `~/.config/opencode/svelte.json` or, if you have an `OPENCODE_CONFIG_DIR` environment variable specified, at `$OPENCODE_CONFIG_DIR/svelte.json`.

View File

@@ -0,0 +1,263 @@
## `svelte-code-writer`
CLI tools for Svelte 5 documentation lookup and code analysis. MUST be used whenever creating, editing or analyzing any Svelte component (.svelte) or Svelte module (.svelte.ts/.svelte.js). If possible, this skill should be executed within the svelte-file-editor agent for optimal results.
<a href="https://github.com/sveltejs/ai-tools/releases?q=svelte-code-writer" target="_blank" rel="noopener noreferrer">Open Releases page</a>
<details>
<summary>View skill content</summary>
<!-- prettier-ignore-start -->
````markdown
# Svelte 5 Code Writer
## CLI Tools
You have access to `@sveltejs/mcp` CLI for Svelte-specific assistance. Use these commands via `npx`:
### List Documentation Sections
```bash
npx @sveltejs/mcp list-sections
```
Lists all available Svelte 5 and SvelteKit documentation sections with titles and paths.
### Get Documentation
```bash
npx @sveltejs/mcp get-documentation "<section1>,<section2>,..."
```
Retrieves full documentation for specified sections. Use after `list-sections` to fetch relevant docs.
**Example:**
```bash
npx @sveltejs/mcp get-documentation "$state,$derived,$effect"
```
### Svelte Autofixer
```bash
npx @sveltejs/mcp svelte-autofixer "<code_or_path>" [options]
```
Analyzes Svelte code and suggests fixes for common issues.
**Options:**
- `--async` - Enable async Svelte mode (default: false)
- `--svelte-version` - Target version: 4 or 5 (default: 5)
**Examples:**
```bash
# Analyze inline code (escape $ as \$)
npx @sveltejs/mcp svelte-autofixer '<script>let count = \$state(0);</script>'
# Analyze a file
npx @sveltejs/mcp svelte-autofixer ./src/lib/Component.svelte
# Target Svelte 4
npx @sveltejs/mcp svelte-autofixer ./Component.svelte --svelte-version 4
```
**Important:** When passing code with runes (`$state`, `$derived`, etc.) via the terminal, escape the `$` character as `\$` to prevent shell variable substitution.
## Workflow
1. **Uncertain about syntax?** Run `list-sections` then `get-documentation` for relevant topics
2. **Reviewing/debugging?** Run `svelte-autofixer` on the code to detect issues
3. **Always validate** - Run `svelte-autofixer` before finalizing any Svelte component
````
<!-- prettier-ignore-end -->
</details>
## `svelte-core-bestpractices`
Guidance on writing fast, robust, modern Svelte code. Load this skill whenever in a Svelte project and asked to write/edit or analyze a Svelte component or module. Covers reactivity, event handling, styling, integration with libraries and more.
<a href="https://github.com/sveltejs/ai-tools/releases?q=svelte-core-bestpractices" target="_blank" rel="noopener noreferrer">Open Releases page</a>
<details>
<summary>View skill content</summary>
<!-- prettier-ignore-start -->
````markdown
## `$state`
Only use the `$state` rune for variables that should be _reactive_ — in other words, variables that cause an `$effect`, `$derived` or template expression to update. Everything else can be a normal variable.
Objects and arrays (`$state({...})` or `$state([...])`) are made deeply reactive, meaning mutation will trigger updates. This has a trade-off: in exchange for fine-grained reactivity, the objects must be proxied, which has performance overhead. In cases where you're dealing with large objects that are only ever reassigned (rather than mutated), use `$state.raw` instead. This is often the case with API responses, for example.
## `$derived`
To compute something from state, use `$derived` rather than `$effect`:
```js
// do this
let square = $derived(num * num);
// don't do this
let square;
$effect(() => {
square = num * num;
});
```
> [!NOTE] `$derived` is given an expression, _not_ a function. If you need to use a function (because the expression is complex, for example) use `$derived.by`.
Deriveds are writable — you can assign to them, just like `$state`, except that they will re-evaluate when their expression changes.
If the derived expression is an object or array, it will be returned as-is — it is _not_ made deeply reactive. You can, however, use `$state` inside `$derived.by` in the rare cases that you need this.
## `$effect`
Effects are an escape hatch and should mostly be avoided. In particular, avoid updating state inside effects.
- If you need to sync state to an external library such as D3, it is often neater to use [`{@attach ...}`](references/@attach.md)
- If you need to run some code in response to user interaction, put the code directly in an event handler or use a [function binding](references/bind.md) as appropriate
- If you need to log values for debugging purposes, use [`$inspect`](references/$inspect.md)
- If you need to observe something external to Svelte, use [`createSubscriber`](references/svelte-reactivity.md)
Never wrap the contents of an effect in `if (browser) {...}` or similar — effects do not run on the server.
## `$props`
Treat props as though they will change. For example, values that depend on props should usually use `$derived`:
```js
// @errors: 2451
let { type } = $props();
// do this
let color = $derived(type === 'danger' ? 'red' : 'green');
// don't do this — `color` will not update if `type` changes
let color = type === 'danger' ? 'red' : 'green';
```
## `$inspect.trace`
`$inspect.trace` is a debugging tool for reactivity. If something is not updating properly or running more than it should you can add `$inspect.trace(label)` as the first line of an `$effect` or `$derived.by` (or any function they call) to trace their dependencies and discover which one triggered an update.
## Events
Any element attribute starting with `on` is treated as an event listener:
```svelte
<button onclick={() => {...}}>click me</button>
<!-- attribute shorthand also works -->
<button {onclick}>...</button>
<!-- so do spread attributes -->
<button {...props}>...</button>
```
If you need to attach listeners to `window` or `document` you can use `<svelte:window>` and `<svelte:document>`:
```svelte
<svelte:window onkeydown={...} />
<svelte:document onvisibilitychange={...} />
```
Avoid using `onMount` or `$effect` for this.
## Snippets
[Snippets](references/snippet.md) are a way to define reusable chunks of markup that can be instantiated with the [`{@render ...}`](references/@render.md) tag, or passed to components as props. They must be declared within the template.
```svelte
{#snippet greeting(name)}
<p>hello {name}!</p>
{/snippet}
{@render greeting('world')}
```
> [!NOTE] Snippets declared at the top level of a component (i.e. not inside elements or blocks) can be referenced inside `<script>`. A snippet that doesn't reference component state is also available in a `<script module>`, in which case it can be exported for use by other components.
## Each blocks
Prefer to use [keyed each blocks](references/each.md) — this improves performance by allowing Svelte to surgically insert or remove items rather than updating the DOM belonging to existing items.
> [!NOTE] The key _must_ uniquely identify the object. Do not use the index as a key.
Avoid destructuring if you need to mutate the item (with something like `bind:value={item.count}`, for example).
## Using JavaScript variables in CSS
If you have a JS variable that you want to use inside CSS you can set a CSS custom property with the `style:` directive.
```svelte
<div style:--columns={columns}>...</div>
```
You can then reference `var(--columns)` inside the component's `<style>`.
## Styling child components
The CSS in a component's `<style>` is scoped to that component. If a parent component needs to control the child's styles, the preferred way is to use CSS custom properties:
```svelte
<!-- Parent.svelte -->
<Child --color="red" />
<!-- Child.svelte -->
<h1>Hello</h1>
<style>
h1 {
color: var(--color);
}
</style>
```
If this is impossible (for example, the child component comes from a library) you can use `:global` to override styles:
```svelte
<div>
<Child />
</div>
<style>
div :global {
h1 {
color: red;
}
}
</style>
```
## Context
Consider using context instead of declaring state in a shared module. This will scope the state to the part of the app that needs it, and eliminate the possibility of it leaking between users when server-side rendering.
Use `createContext` rather than `setContext` and `getContext`, as it provides type safety.
## Async Svelte
If using version 5.36 or higher, you can use [await expressions](references/await-expressions.md) and [hydratable](references/hydratable.md) to use promises directly inside components. Note that these require the `experimental.async` option to be enabled in `svelte.config.js` as they are not yet considered fully stable.
## Avoid legacy features
Always use runes mode for new code, and avoid features that have more modern replacements:
- use `$state` instead of implicit reactivity (e.g. `let count = 0; count += 1`)
- use `$derived` and `$effect` instead of `$:` assignments and statements (but only use effects when there is no better solution)
- use `$props` instead of `export let`, `$$props` and `$$restProps`
- use `onclick={...}` instead of `on:click={...}`
- use `{#snippet ...}` and `{@render ...}` instead of `<slot>` and `$$slots` and `<svelte:fragment>`
- use `<DynamicComponent>` instead of `<svelte:component this={DynamicComponent}>`
- use `import Self from './ThisComponent.svelte'` and `<Self>` instead of `<svelte:self>`
- use classes with `$state` fields to share reactivity between components, instead of using stores
- use `{@attach ...}` instead of `use:action`
- use clsx-style arrays and objects in `class` attributes, instead of the `class:` directive
````
<!-- prettier-ignore-end -->
</details>

View File

@@ -6,81 +6,6 @@ This is the list of available skills provided by the Svelte MCP package. Skills
Skills are available in both the Claude Code plugin (installed via the marketplace) and the OpenCode plugin (`@sveltejs/opencode`). They can also be manually installed in your `.claude/skills/` or `.opencode/skills/` folder.
You can download the latest skills from the [releases page](https://github.com/sveltejs/mcp/releases) or find them in the [`plugins/svelte/skills`](https://github.com/sveltejs/mcp/tree/main/plugins/svelte/skills) folder.
You can download the latest skills from the [releases page](https://github.com/sveltejs/ai-tools/releases) or find them in the [`plugins/svelte/skills`](https://github.com/sveltejs/ai-tools/tree/main/plugins/svelte/skills) folder.
## `svelte-code-writer`
CLI tools for Svelte 5 documentation lookup and code analysis. MUST be used whenever creating or editing any Svelte component (.svelte) or Svelte module (.svelte.ts/.svelte.js). If possible, this skill should be executed within the svelte-file-editor agent for optimal results.
<a href="https://github.com/sveltejs/mcp/releases?q=svelte-code-writer" target="_blank" rel="noopener noreferrer">Open Releases page</a>
<details>
<summary>View skill content</summary>
<!-- prettier-ignore-start -->
````markdown
# Svelte 5 Code Writer
## CLI Tools
You have access to `@sveltejs/mcp` CLI for Svelte-specific assistance. Use these commands via `npx`:
### List Documentation Sections
```bash
npx @sveltejs/mcp list-sections
```
Lists all available Svelte 5 and SvelteKit documentation sections with titles and paths.
### Get Documentation
```bash
npx @sveltejs/mcp get-documentation "<section1>,<section2>,..."
```
Retrieves full documentation for specified sections. Use after `list-sections` to fetch relevant docs.
**Example:**
```bash
npx @sveltejs/mcp get-documentation "$state,$derived,$effect"
```
### Svelte Autofixer
```bash
npx @sveltejs/mcp svelte-autofixer "<code_or_path>" [options]
```
Analyzes Svelte code and suggests fixes for common issues.
**Options:**
- `--async` - Enable async Svelte mode (default: false)
- `--svelte-version` - Target version: 4 or 5 (default: 5)
**Examples:**
```bash
# Analyze inline code (escape $ as \$)
npx @sveltejs/mcp svelte-autofixer '<script>let count = \$state(0);</script>'
# Analyze a file
npx @sveltejs/mcp svelte-autofixer ./src/lib/Component.svelte
# Target Svelte 4
npx @sveltejs/mcp svelte-autofixer ./Component.svelte --svelte-version 4
```
**Important:** When passing code with runes (`$state`, `$derived`, etc.) via the terminal, escape the `$` character as `\$` to prevent shell variable substitution.
## Workflow
1. **Uncertain about syntax?** Run `list-sections` then `get-documentation` for relevant topics
2. **Reviewing/debugging?** Run `svelte-autofixer` on the code to detect issues
3. **Always validate** - Run `svelte-autofixer` before finalizing any Svelte component
````
<!-- prettier-ignore-end -->
</details>
@include .generated/skills.md

View File

@@ -1,3 +1,3 @@
---
title: MCP
title: AI
---

View File

@@ -25,7 +25,14 @@
"debug:generate-summaries": "pnpm --filter @sveltejs/mcp-server run debug:generate-summaries",
"release": "pnpm --filter @sveltejs/mcp run build && changeset publish",
"changeset:version": "changeset version && pnpm --filter @sveltejs/mcp run update:version && git add --all",
"sync-opencode-skills": "rm -rf packages/opencode/skills && cp -r plugins/svelte/skills packages/opencode/skills"
"sync-plugins": "pnpm sync-claude-plugin && pnpm sync-cursor-plugin && pnpm sync-opencode-plugin && pnpm bump-plugin-versions",
"sync-claude-plugin": "node scripts/sync-claude-plugin.ts",
"sync-cursor-plugin": "node scripts/sync-cursor-plugin.ts",
"sync-opencode-plugin": "node scripts/sync-opencode-plugin.ts && pnpm generate-opencode-jsonschema",
"bump-plugin-versions": "node scripts/bump-plugin-versions.ts",
"postbump-plugin-versions": "pnpm format",
"resolve-references": "node scripts/resolve-references.ts",
"postresolve-references": "pnpm format"
},
"keywords": [
"svelte",

View File

@@ -344,87 +344,111 @@ describe('add_autofixers_issues', () => {
});
describe('imported_runes', () => {
describe.each([{ source: 'svelte' }, { source: 'svelte/runes' }])(
'from "$source"',
({ source }) => {
describe.each(dollarless_runes)('single import ($rune)', ({ rune }) => {
it(`should add suggestions when importing '${rune}' from '${source}'`, () => {
const content = run_autofixers_on_code(`
describe.each([
{ source: 'svelte' },
{ source: 'svelte/runes' },
{ source: '@sveltejs/runes' },
{ source: '@sveltejs/vite-plugin-svelte' },
])('from "$source"', ({ source }) => {
describe.each(dollarless_runes)('single import ($rune)', ({ rune }) => {
it(`should add suggestions when importing '${rune}' from '${source}'`, () => {
const content = run_autofixers_on_code(`
<script>
import { ${rune} } from '${source}';
</script>`);
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
expect(content.suggestions).toContain(
`You are importing "${rune}" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$${rune}" directly.`,
);
});
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
expect(content.suggestions).toContain(
`You are importing "${rune}" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$${rune}" directly.`,
);
});
it(`should add suggestions when importing "${rune}" as the default export from '${source}'`, () => {
const content = run_autofixers_on_code(`
it(`should add suggestions when importing "${rune}" as the default export from '${source}'`, () => {
const content = run_autofixers_on_code(`
<script>
import ${rune} from '${source}';
</script>`);
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
expect(content.suggestions).toContain(
`You are importing "${rune}" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$${rune}" directly.`,
);
});
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
expect(content.suggestions).toContain(
`You are importing "${rune}" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$${rune}" directly.`,
);
});
it(`should add suggestions when importing '${rune}' as the namespace export from '${source}'`, () => {
const content = run_autofixers_on_code(`
it(`should add suggestions when importing '${rune}' as the namespace export from '${source}'`, () => {
const content = run_autofixers_on_code(`
<script>
import * as ${rune} from '${source}';
</script>`);
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
expect(content.suggestions).toContain(
`You are importing "${rune}" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$${rune}" directly.`,
);
});
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
expect(content.suggestions).toContain(
`You are importing "${rune}" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$${rune}" directly.`,
);
});
});
it(`should add suggestions when importing multiple runes from '${source}'`, () => {
const content = run_autofixers_on_code(`
it(`should add suggestions when importing multiple runes from '${source}'`, () => {
const content = run_autofixers_on_code(`
<script>
import { onMount, state, effect } from '${source}';
</script>`);
expect(content.suggestions.length).toBeGreaterThanOrEqual(2);
expect(content.suggestions).toContain(
`You are importing "state" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$state" directly.`,
);
expect(content.suggestions).toContain(
`You are importing "effect" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$effect" directly.`,
);
});
expect(content.suggestions.length).toBeGreaterThanOrEqual(2);
expect(content.suggestions).toContain(
`You are importing "state" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$state" directly.`,
);
expect(content.suggestions).toContain(
`You are importing "effect" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$effect" directly.`,
);
});
it(`should not add suggestions when importing other identifiers from '${source}'`, () => {
const content = run_autofixers_on_code(`
it(`should not add suggestions when importing other identifiers from '${source}'`, () => {
const content = run_autofixers_on_code(`
<script>
import { onMount } from '${source}';
</script>`);
expect(content.suggestions).not.toContain(
`You are importing "onMount" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$onMount" directly.`,
);
});
},
);
expect(content.suggestions).not.toContain(
`You are importing "onMount" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$onMount" directly.`,
);
});
});
describe.each(dollarless_runes)('importing $rune from external lib', ({ rune }) => {
it(`should not add suggestions when importing from packages that are not svelte`, () => {
it(`should not add suggestions when importing from packages whose name doesn't contain svelte`, () => {
const content = run_autofixers_on_code(`
<script>
import { ${rune} } from 'something-something';
</script>`);
expect(content.suggestions).not.toContain(
`You are importing "${rune}" from "something-something". This is not necessary, all runes are globally available. Please remove this import and use "$${rune}" directly.`,
);
});
it(`should add suggestions with a different hint when importing from packages whose name contains svelte but it's not official`, () => {
const content = run_autofixers_on_code(`
<script>
import { ${rune} } from 'svelte-something-something';
</script>`);
expect(content.suggestions).not.toContain(
`You are importing "${rune}" from "svelte-something-something". This is not necessary, all runes are globally available. Please remove this import and use "$${rune}" directly.`,
expect(content.suggestions).toContain(
`You are importing "${rune}" from "svelte-something-something". If you are trying to import runes to use them this is not necessary, all runes are globally available. Please remove this import and use "$${rune}" directly. If you are importing the function from a separate library ignore this suggestion.`,
);
});
});
it('should not add the imported_runes suggestion when importing derived from svelte/store', () => {
const content = run_autofixers_on_code(`
<script>
import { derived } from 'svelte/store';
</script>`);
expect(content.suggestions).not.toContain(
'You are importing "derived" from "svelte/store". This is not necessary, all runes are globally available. Please remove this import and use "$derived" directly.',
);
});
});
describe('derived_with_function', () => {

View File

@@ -3,10 +3,20 @@ import type { Autofixer } from './index.js';
const dollarless_runes = base_runes.map((r) => r.replace('$', ''));
function should_suggest_for_source(source: string, rune: string) {
if (!source.includes('svelte')) {
return false;
}
if (source === 'svelte/store' && rune === 'derived') {
return false;
}
return true;
}
export const imported_runes: Autofixer = {
ImportDeclaration(node, { state, next }) {
const source = (node.source.value || node.source.raw?.slice(1, -1))?.toString();
if (source && (source === 'svelte' || source.startsWith('svelte/'))) {
if (source) {
for (const specifier of node.specifiers) {
const id =
specifier.type === 'ImportDefaultSpecifier'
@@ -16,10 +26,25 @@ export const imported_runes: Autofixer = {
: specifier.type === 'ImportSpecifier'
? specifier.imported
: null;
if (id && id.type === 'Identifier' && dollarless_runes.includes(id.name)) {
state.output.suggestions.push(
`You are importing "${id.name}" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$${id.name}" directly.`,
);
if (
id &&
id.type === 'Identifier' &&
dollarless_runes.includes(id.name) &&
should_suggest_for_source(source, id.name)
) {
if (
source === 'svelte' ||
source.startsWith('svelte/') ||
source.startsWith('@sveltejs')
) {
state.output.suggestions.push(
`You are importing "${id.name}" from "${source}". This is not necessary, all runes are globally available. Please remove this import and use "$${id.name}" directly.`,
);
} else {
state.output.suggestions.push(
`You are importing "${id.name}" from "${source}". If you are trying to import runes to use them this is not necessary, all runes are globally available. Please remove this import and use "$${id.name}" directly. If you are importing the function from a separate library ignore this suggestion.`,
);
}
}
}
}

View File

@@ -91,16 +91,56 @@ export async function get_documentation_handler({
}
});
const has_any_success = results.some((result) => result.success);
let final_text = results.map((r) => r.content).join('\n\n---\n\n');
const successes = results.filter((r) => r.success);
const failed_sections = sections.filter(
(s) =>
!available_sections.some(
(a) => a.title.toLowerCase() === s.toLowerCase() || a.slug === s || a.url === s,
),
);
if (!has_any_success) {
const formatted_sections = await format_sections_list();
final_text += `\n\n---\n\n${SECTIONS_LIST_INTRO}\n\n${formatted_sections}\n\n${SECTIONS_LIST_OUTRO}`;
if (successes.length > 0 && failed_sections.length === 0) {
return successes.map((r) => r.content).join('\n\n---\n\n');
}
return final_text;
const parts: string[] = [];
if (successes.length > 0) {
parts.push(successes.map((r) => r.content).join('\n\n---\n\n'));
}
const fuzzy_results = failed_sections.map((requested) => {
const lower = requested.toLowerCase();
const matches = available_sections.filter(
(a) =>
a.title.toLowerCase().includes(lower) ||
a.slug.includes(lower) ||
lower.includes(a.slug.split('/').pop() ?? '') ||
a.use_cases.toLowerCase().includes(lower),
);
return { requested, matches };
});
const has_fuzzy = fuzzy_results.some((r) => r.matches.length > 0);
// Full list only when no successes and no fuzzy matches
if (successes.length === 0 && !has_fuzzy) {
const formatted_sections = await format_sections_list();
parts.push(`${SECTIONS_LIST_INTRO}\n\n${formatted_sections}\n\n${SECTIONS_LIST_OUTRO}`);
}
// Similar results then errors
for (const { requested, matches } of fuzzy_results) {
if (matches.length > 0) {
const match_list = matches.map((m) => `- title: ${m.title}, section: ${m.slug}`).join('\n');
parts.push(
`${matches.length} similar result${matches.length > 1 ? 's' : ''} for "${requested}":\n${match_list}`,
);
}
parts.push(`Section not found: "${requested}".`);
}
return parts.join('\n\n---\n\n');
}
export function get_documentation(server: SvelteMcp) {

View File

@@ -1,169 +1,187 @@
# @sveltejs/mcp
## 0.1.22
### Patch Changes
- fix: broaden checks for imported runes because LLMs are unhinged ([#185](https://github.com/sveltejs/ai-tools/pull/185))
## 0.1.21
### Patch Changes
- feat: display similar result & error at the end ([#161](https://github.com/sveltejs/ai-tools/pull/161))
## 0.1.20
### Patch Changes
- fix: turn off no-inspect in eslint for mcp ([`2245cb2`](https://github.com/sveltejs/ai-tools/commit/2245cb2dc9e2d217869b6a800795ce59ffb40c51))
## 0.1.19
### Patch Changes
- chore: update svelte ([`7447744`](https://github.com/sveltejs/mcp/commit/74477448cea44ec21684ea4d39f2c5c7133b5150))
- chore: update svelte ([`7447744`](https://github.com/sveltejs/ai-tools/commit/74477448cea44ec21684ea4d39f2c5c7133b5150))
## 0.1.18
### Patch Changes
- feat: expose playground link as MCP App ([#138](https://github.com/sveltejs/mcp/pull/138))
- feat: expose playground link as MCP App ([#138](https://github.com/sveltejs/ai-tools/pull/138))
## 0.1.17
### Patch Changes
- fix: add suggestion for snippets declared in script tag ([#132](https://github.com/sveltejs/mcp/pull/132))
- fix: add suggestion for snippets declared in script tag ([#132](https://github.com/sveltejs/ai-tools/pull/132))
## 0.1.16
### Patch Changes
- feat: expose tools as JS api + cli ([#128](https://github.com/sveltejs/mcp/pull/128))
- feat: expose tools as JS api + cli ([#128](https://github.com/sveltejs/ai-tools/pull/128))
## 0.1.15
### Patch Changes
- fix: server.json version + update publisher ([`9dfb4de`](https://github.com/sveltejs/mcp/commit/9dfb4dedb42837c40c4e660f0f816d7cf9081fc4))
- fix: server.json version + update publisher ([`9dfb4de`](https://github.com/sveltejs/ai-tools/commit/9dfb4dedb42837c40c4e660f0f816d7cf9081fc4))
## 0.1.14
### Patch Changes
- fix: improve prompt to reduce token usage ([#124](https://github.com/sveltejs/mcp/pull/124))
- fix: improve prompt to reduce token usage ([#124](https://github.com/sveltejs/ai-tools/pull/124))
## 0.1.13
### Patch Changes
- fix: revert name change and add title ([`98efa1e`](https://github.com/sveltejs/mcp/commit/98efa1e09ebcca7827b10dc6bc8e1699fc1e5171))
- fix: revert name change and add title ([`98efa1e`](https://github.com/sveltejs/ai-tools/commit/98efa1e09ebcca7827b10dc6bc8e1699fc1e5171))
## 0.1.12
### Patch Changes
- fix: update server name on mcp registry ([`60297b3`](https://github.com/sveltejs/mcp/commit/60297b3c49bf110b48908e61b5d5d902ea1bdf39))
- fix: update server name on mcp registry ([`60297b3`](https://github.com/sveltejs/ai-tools/commit/60297b3c49bf110b48908e61b5d5d902ea1bdf39))
- chore: update tmcp ([#99](https://github.com/sveltejs/mcp/pull/99))
- chore: update tmcp ([#99](https://github.com/sveltejs/ai-tools/pull/99))
## 0.1.11
### Patch Changes
- fix: add `async` parameter to `svelte-autofixer` ([#94](https://github.com/sveltejs/mcp/pull/94))
- fix: add `async` parameter to `svelte-autofixer` ([#94](https://github.com/sveltejs/ai-tools/pull/94))
- fix: install latest eslint svelte packages to support `$state.eager` ([`f6ce89f`](https://github.com/sveltejs/mcp/commit/f6ce89ff34faabc3d746a350ea347298ecfed2ec))
- fix: install latest eslint svelte packages to support `$state.eager` ([`f6ce89f`](https://github.com/sveltejs/ai-tools/commit/f6ce89ff34faabc3d746a350ea347298ecfed2ec))
## 0.1.10
### Patch Changes
- fix: add icons to `server.json` ([`02c951b`](https://github.com/sveltejs/mcp/commit/02c951baa86ac8103ffc158a202c06cfe6b15c01))
- fix: add icons to `server.json` ([`02c951b`](https://github.com/sveltejs/ai-tools/commit/02c951baa86ac8103ffc158a202c06cfe6b15c01))
- fix: add `preferred-frame-size` to UI resource ([`3fabcc0`](https://github.com/sveltejs/mcp/commit/3fabcc0f9bfee916c0deb9c2ffa931ed2168af2d))
- fix: add `preferred-frame-size` to UI resource ([`3fabcc0`](https://github.com/sveltejs/ai-tools/commit/3fabcc0f9bfee916c0deb9c2ffa931ed2168af2d))
- feat: support: `$state.eager` ([#90](https://github.com/sveltejs/mcp/pull/90))
- feat: support: `$state.eager` ([#90](https://github.com/sveltejs/ai-tools/pull/90))
## 0.1.9
### Patch Changes
- feat: return `mcp-ui` resource from `playground-link` ([#84](https://github.com/sveltejs/mcp/pull/84))
- feat: return `mcp-ui` resource from `playground-link` ([#84](https://github.com/sveltejs/ai-tools/pull/84))
- feat: suggest against js variables in css ([#78](https://github.com/sveltejs/mcp/pull/78))
- feat: suggest against js variables in css ([#78](https://github.com/sveltejs/ai-tools/pull/78))
## 0.1.8
### Patch Changes
- fix: upgrade registry publisher cli ([`5fa2baa`](https://github.com/sveltejs/mcp/commit/5fa2baa27009f01e0e4e91cee7984b81a81c1c29))
- fix: upgrade registry publisher cli ([`5fa2baa`](https://github.com/sveltejs/ai-tools/commit/5fa2baa27009f01e0e4e91cee7984b81a81c1c29))
## 0.1.7
### Patch Changes
- fix: use correct server schema version ([`579be87`](https://github.com/sveltejs/mcp/commit/579be877fa9f87f7f173450ca5bc918824d68282))
- fix: use correct server schema version ([`579be87`](https://github.com/sveltejs/ai-tools/commit/579be877fa9f87f7f173450ca5bc918824d68282))
## 0.1.6
### Patch Changes
- fix: prevent `imported_runes` suggestion from being added for libs that are not svelte ([`87af64f`](https://github.com/sveltejs/mcp/commit/87af64f4bc6d07b75640eb987a33655654363997))
- fix: prevent `imported_runes` suggestion from being added for libs that are not svelte ([`87af64f`](https://github.com/sveltejs/ai-tools/commit/87af64f4bc6d07b75640eb987a33655654363997))
- feat: add svelte icon and website url for mcp server ([#75](https://github.com/sveltejs/mcp/pull/75))
- feat: add svelte icon and website url for mcp server ([#75](https://github.com/sveltejs/ai-tools/pull/75))
- fix: use `data:` uri for local icon & add icons to tools + resources + prompts ([`cf62286`](https://github.com/sveltejs/mcp/commit/cf622869129382a97ad059bb1389f115907adc8e))
- fix: use `data:` uri for local icon & add icons to tools + resources + prompts ([`cf62286`](https://github.com/sveltejs/ai-tools/commit/cf622869129382a97ad059bb1389f115907adc8e))
## 0.1.5
### Patch Changes
- fix: widen `desired_svelte_version` validation to accommodate some clients ([`3b301d7`](https://github.com/sveltejs/mcp/commit/3b301d7d9c2f49758023408f505bc4ca79caaff4))
- fix: widen `desired_svelte_version` validation to accommodate some clients ([`3b301d7`](https://github.com/sveltejs/ai-tools/commit/3b301d7d9c2f49758023408f505bc4ca79caaff4))
- fix: minor tweaks to the prompt to allow for automatic sync ([#63](https://github.com/sveltejs/mcp/pull/63))
- fix: minor tweaks to the prompt to allow for automatic sync ([#63](https://github.com/sveltejs/ai-tools/pull/63))
- feat: `read_state_with_dollar` autofixer ([#66](https://github.com/sveltejs/mcp/pull/66))
- feat: `read_state_with_dollar` autofixer ([#66](https://github.com/sveltejs/ai-tools/pull/66))
## 0.1.4
### Patch Changes
- fix: pass secrets in action and update `mcpName` ([`044f098`](https://github.com/sveltejs/mcp/commit/044f0988b935fff39911a861a648dfb276f5831a))
- fix: pass secrets in action and update `mcpName` ([`044f098`](https://github.com/sveltejs/ai-tools/commit/044f0988b935fff39911a861a648dfb276f5831a))
## 0.1.3
### Patch Changes
- fix: use DNS to publish MCP ([#59](https://github.com/sveltejs/mcp/pull/59))
- fix: use DNS to publish MCP ([#59](https://github.com/sveltejs/ai-tools/pull/59))
## 0.1.2
### Patch Changes
- fix: publish to MCP registry (I really hope this time for real) ([`ef5241c`](https://github.com/sveltejs/mcp/commit/ef5241cbc204ad8bb84bde27db7c9d0a08280245))
- fix: publish to MCP registry (I really hope this time for real) ([`ef5241c`](https://github.com/sveltejs/ai-tools/commit/ef5241cbc204ad8bb84bde27db7c9d0a08280245))
## 0.1.1
### Patch Changes
- feat: publish mcp to registry (maybe for real this time) ([`132943d`](https://github.com/sveltejs/mcp/commit/132943db3b04dbbd322d08926c0880c990a61f5f))
- feat: publish mcp to registry (maybe for real this time) ([`132943d`](https://github.com/sveltejs/ai-tools/commit/132943db3b04dbbd322d08926c0880c990a61f5f))
## 0.1.0
### Minor Changes
- feat: publish to registry ([#45](https://github.com/sveltejs/mcp/pull/45))
- feat: publish to registry ([#45](https://github.com/sveltejs/ai-tools/pull/45))
### Patch Changes
- feat: add autofixer to tell the LLM to check if some function called in effect is assigning state #26 ([`73d7625`](https://github.com/sveltejs/mcp/commit/73d7625b3ca6a812ba91883ea668d80ff1e7c703))
- feat: add autofixer to tell the LLM to check if some function called in effect is assigning state #26 ([`73d7625`](https://github.com/sveltejs/ai-tools/commit/73d7625b3ca6a812ba91883ea668d80ff1e7c703))
- feat: add bind:this -> attachment and action -> attachment autofixer #20 ([`73d7625`](https://github.com/sveltejs/mcp/commit/73d7625b3ca6a812ba91883ea668d80ff1e7c703))
- feat: add bind:this -> attachment and action -> attachment autofixer #20 ([`73d7625`](https://github.com/sveltejs/ai-tools/commit/73d7625b3ca6a812ba91883ea668d80ff1e7c703))
## 0.0.4
### Patch Changes
- fix: allow TS `.svelte.ts` modules ([#49](https://github.com/sveltejs/mcp/pull/49))
- fix: allow TS `.svelte.ts` modules ([#49](https://github.com/sveltejs/ai-tools/pull/49))
## 0.0.3
### Patch Changes
- fix: check effect.pre in assign-in-effect ([#41](https://github.com/sveltejs/mcp/pull/41))
- fix: check effect.pre in assign-in-effect ([#41](https://github.com/sveltejs/ai-tools/pull/41))
- feat: `use_cases` documentation metadata ([#29](https://github.com/sveltejs/mcp/pull/29))
- feat: `use_cases` documentation metadata ([#29](https://github.com/sveltejs/ai-tools/pull/29))
- fix: change title names to allow for claude code to use the prompt ([`725f785`](https://github.com/sveltejs/mcp/commit/725f785766d04e9ed810a7c3f6bcfdb2e2b8234c))
- fix: change title names to allow for claude code to use the prompt ([`725f785`](https://github.com/sveltejs/ai-tools/commit/725f785766d04e9ed810a7c3f6bcfdb2e2b8234c))
- fix: enable doc tools ([`cb316c5`](https://github.com/sveltejs/mcp/commit/cb316c5b3ebc712946969d2d57236d159e796d58))
- fix: enable doc tools ([`cb316c5`](https://github.com/sveltejs/ai-tools/commit/cb316c5b3ebc712946969d2d57236d159e796d58))
## 0.0.2
### Patch Changes
- feat: latest version ([#25](https://github.com/sveltejs/mcp/pull/25))
- feat: latest version ([#25](https://github.com/sveltejs/ai-tools/pull/25))

View File

@@ -1,19 +1,19 @@
{
"name": "@sveltejs/mcp",
"version": "0.1.19",
"version": "0.1.22",
"type": "module",
"license": "MIT",
"mcpName": "dev.svelte/mcp",
"homepage": "https://github.com/sveltejs/mcp#readme",
"homepage": "https://github.com/sveltejs/ai-tools#readme",
"bugs": {
"url": "https://github.com/sveltejs/mcp/issues"
"url": "https://github.com/sveltejs/ai-tools/issues"
},
"bin": {
"svelte-mcp": "./dist/index.mjs"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sveltejs/mcp.git",
"url": "git+https://github.com/sveltejs/ai-tools.git",
"path": "packages/mcp-stdio"
},
"files": [

View File

@@ -5,11 +5,11 @@
"description": "The official Svelte MCP server providing docs and autofixing tools for Svelte development",
"repository": {
"id": "1054419133",
"url": "https://github.com/sveltejs/mcp",
"url": "https://github.com/sveltejs/ai-tools",
"subfolder": "packages/mcp-stdio",
"source": "github"
},
"version": "0.1.19",
"version": "0.1.22",
"websiteUrl": "https://svelte.dev/docs/mcp/overview",
"icons": [
{
@@ -25,7 +25,7 @@
{
"registryType": "npm",
"identifier": "@sveltejs/mcp",
"version": "0.1.19",
"version": "0.1.22",
"runtimeHint": "npx",
"transport": {
"type": "stdio"

View File

@@ -1,13 +1,73 @@
# @sveltejs/opencode
## 0.1.8
### Patch Changes
- fix: add `server` export to opencode plugin ([`96c50ac`](https://github.com/sveltejs/ai-tools/commit/96c50acae2b4131a6c72d3579a73c44ab9158b18))
## 0.1.7
### Patch Changes
- fix: import `ts` files directly ([#190](https://github.com/sveltejs/ai-tools/pull/190))
## 0.1.6
### Patch Changes
- chore: sync skills from svelte.dev ([#178](https://github.com/sveltejs/ai-tools/pull/178))
- fix: update svelte-file-editor agent to use proper name ([#183](https://github.com/sveltejs/ai-tools/pull/183))
## 0.1.5
### Patch Changes
- fix: merge user-configured svelte-file-editor agent settings ([#176](https://github.com/sveltejs/ai-tools/pull/176))
- feat(opencode): mcp enabled option is passed to opencode ([#171](https://github.com/sveltejs/ai-tools/pull/171))
- feat: allow enabling a specific skill in opencode plugin ([#174](https://github.com/sveltejs/ai-tools/pull/174))
## 0.1.4
### Patch Changes
- feat: `svelte-core-bestpractices` skill ([#162](https://github.com/sveltejs/ai-tools/pull/162))
## 0.1.3
### Patch Changes
- fix: better subagent instructions to use MCP or skill ([#163](https://github.com/sveltejs/ai-tools/pull/163))
## 0.1.2
### Patch Changes
- feat: allow for local opencode config ([#156](https://github.com/sveltejs/ai-tools/pull/156))
## 0.1.1
### Patch Changes
- fix: actually push skills to right config path ([`c2c1b3e`](https://github.com/sveltejs/ai-tools/commit/c2c1b3e5e788b14eea17cd37a83ca55433cc4072))
## 0.1.0
### Minor Changes
- feat: distribute skills through opencode plugin ([#151](https://github.com/sveltejs/ai-tools/pull/151))
## 0.0.3
### Patch Changes
- chore: add README to opencode plugin ([`71295bc`](https://github.com/sveltejs/mcp/commit/71295bc11fb7bac6703e655f5fddead29967353c))
- chore: add README to opencode plugin ([`71295bc`](https://github.com/sveltejs/ai-tools/commit/71295bc11fb7bac6703e655f5fddead29967353c))
## 0.0.2
### Patch Changes
- fix: show toast after a few seconds to avoid race condition ([`57e2d1d`](https://github.com/sveltejs/mcp/commit/57e2d1def1f5590d0a3dd6d269ac39f6397ffecf))
- fix: show toast after a few seconds to avoid race condition ([`57e2d1d`](https://github.com/sveltejs/ai-tools/commit/57e2d1def1f5590d0a3dd6d269ac39f6397ffecf))

View File

@@ -36,39 +36,76 @@ The plugin injects instructions that teach the agent how to effectively use the
## Configuration
The default configuration:
Create `svelte.json` to customize how the plugin configures MCP, the Svelte subagent, instructions, and skills.
```json
{
"$schema": "https://raw.githubusercontent.com/sveltejs/mcp/refs/heads/main/packages/opencode/schema.json",
"$schema": "https://svelte.dev/opencode/schema.json",
"mcp": {
"type": "remote",
"enabled": true
},
"subagent": {
"enabled": true
"enabled": true,
"agents": {
"svelte-file-editor": {
"model": "anthropic/claude-sonnet-4-20250514",
"temperature": 0.7,
"top_p": 0.9,
"maxSteps": 20
}
}
},
"instructions": {
"enabled": true
},
"skills": {
"enabled": ["svelte-code-writer", "svelte-core-bestpractices"]
}
}
```
### Defaults
If omitted, the plugin uses these defaults:
- `mcp.type`: `"remote"`
- `mcp.enabled`: `true`
- `subagent.enabled`: `true`
- `subagent.agents`: `{}`
- `instructions.enabled`: `true`
- `skills.enabled`: `true`
### Configuration Options
| Option | Type | Default | Description |
| ---------------------- | ----------------------- | ---------- | -------------------------------------------------------------------------------- |
| `mcp.type` | `"remote"` \| `"local"` | `"remote"` | Use the remote server at `mcp.svelte.dev` or run locally via `npx @sveltejs/mcp` |
| `mcp.enabled` | `boolean` | `true` | Enable/disable the MCP server |
| `subagent.enabled` | `boolean` | `true` | Enable/disable the Svelte file editor subagent |
| `instructions.enabled` | `boolean` | `true` | Enable/disable agent instructions injection |
| Option | Type | Default | Description |
| ------------------------------------------------ | --------------------- | ---------- | ---------------------------------------------------------------------------------------------- |
| `mcp.type` | `"remote" \| "local"` | `"remote"` | Use `https://mcp.svelte.dev/mcp` (`remote`) or run `@sveltejs/mcp` via `npx` (`local`). |
| `mcp.enabled` | `boolean` | `true` | Enable or disable the Svelte MCP server entry. |
| `subagent.enabled` | `boolean` | `true` | Enable or disable registration of the `svelte-file-editor` subagent. |
| `subagent.agents.svelte-file-editor.model` | `string` | main agent | Override the model used by the Svelte file editor subagent. |
| `subagent.agents.svelte-file-editor.temperature` | `number` | unset | Set temperature for the subagent. |
| `subagent.agents.svelte-file-editor.top_p` | `number` | unset | Set top-p sampling for the subagent. |
| `subagent.agents.svelte-file-editor.maxSteps` | `number` | unlimited | Limit the number of steps the subagent can execute. |
| `instructions.enabled` | `boolean` | `true` | Enable or disable automatic instruction-file injection. |
| `skills.enabled` | `boolean \| string[]` | `true` | Enable all skills (`true`), disable all skills (`false`), or enable only specific skill names. |
### Config File Location
### Supported Skill Names
Place your configuration at one of these locations:
When using `skills.enabled` as an array, these built-in names are currently available:
- `~/.config/opencode/svelte.json` (global)
- `$OPENCODE_CONFIG_DIR/svelte.json` (if `OPENCODE_CONFIG_DIR` is set, takes priority)
- `svelte-code-writer`
- `svelte-core-bestpractices`
### Config File Locations and Precedence
The plugin reads from these files (lowest priority first, highest priority last):
- `~/.config/opencode/svelte.json`
- `$OPENCODE_CONFIG_DIR/svelte.json` (when `OPENCODE_CONFIG_DIR` is set)
- `.opencode/svelte.json` in the current project
If the same key is defined in multiple files, the later location overrides earlier ones.
## License

View File

@@ -0,0 +1,11 @@
// This file is auto-generated by scripts/sync-opencode-plugin.ts
// Do not edit manually — edit the markdown files in tools/agents/ instead.
export const agents = {
'svelte-file-editor': {
description:
'Specialized Svelte 5 code editor. MUST BE USED PROACTIVELY when creating, editing, or reviewing any .svelte file or .svelte.ts/.svelte.js module and MUST use the tools from the MCP server or the `svelte-file-editor` skill if they are available. Fetches relevant documentation and validates code using the Svelte MCP server tools.',
prompt:
"You are a Svelte 5 expert responsible for writing, editing, and validating Svelte components and modules. You have access to the Svelte MCP server which provides documentation and code analysis tools. Always use the tools from the svelte MCP server to fetch documentation with `get_documentation` and validating the code with `svelte_autofixer`. If the autofixer returns any issue or suggestions try to solve them.\n\nIf the MCP tools are not available you can use the `svelte-code-writer` skill to learn how to use the `@sveltejs/mcp` cli to access the same tools.\n\nIf the skill is not available you can run `npx @sveltejs/mcp@latest -y --help` to learn how to use it.\n\n## Available MCP Tools\n\n### 1. list-sections\n\nLists all available Svelte 5 and SvelteKit documentation sections with titles and paths. Use this first to discover what documentation is available.\n\n### 2. get-documentation\n\nRetrieves full documentation for specified sections. Accepts a single section name or an array of section names. Use after `list-sections` to fetch relevant docs for the task at hand.\n\n**Example sections:** `$state`, `$derived`, `$effect`, `$props`, `$bindable`, `snippets`, `routing`, `load functions`\n\n### 3. svelte-autofixer\n\nAnalyzes Svelte code and returns suggestions to fix issues. Pass the component code directly to this tool. It will detect common mistakes like:\n\n- Using `$effect` instead of `$derived` for computations\n- Missing cleanup in effects\n- Svelte 4 syntax (`on:click`, `export let`, `<slot>`)\n- Missing keys in `{#each}` blocks\n- And more\n\n## Workflow\n\nWhen invoked to work on a Svelte file:\n\n### 1. Gather Context (if needed)\n\nIf you're uncertain about Svelte 5 syntax or patterns, use the MCP tools:\n\n1. Call `list-sections` to see available documentation\n2. Call `get-documentation` with relevant section names\n\n### 2. Read the Target File\n\nRead the file to understand the current implementation.\n\n### 3. Make Changes\n\nApply edits following Svelte 5 best practices:\n\n### 4. Validate Changes\n\nAfter editing, ALWAYS call `svelte-autofixer` with the updated code to check for issues.\n\n### 5. Fix Any Issues\n\nIf the autofixer reports problems, fix them and re-validate until no issues remain.\n\n## Output Format\n\nAfter completing your work, provide:\n\n1. Summary of changes made\n2. Any issues found and fixed by the autofixer\n3. Recommendations for further improvements (if any)",
},
} as const;

View File

@@ -4,6 +4,28 @@ import { homedir } from 'os';
import { join } from 'path';
import * as v from 'valibot';
// Schema for individual agent configuration
const agent_config_schema = v.object({
model: v.pipe(
v.optional(v.string()),
v.description('Model identifier for the agent (e.g., "anthropic/claude-sonnet-4-20250514")'),
),
temperature: v.pipe(
v.optional(v.number()),
v.description('Temperature setting for the agent (e.g., 0.7)'),
),
top_p: v.pipe(
v.optional(v.number()),
v.description(
'Control response diversity with the top_p option. Alternative to temperature for controlling randomness.',
),
),
maxSteps: v.pipe(
v.optional(v.number()),
v.description('Maximum number of steps the agent can take (e.g., 10)'),
),
});
const default_config = {
mcp: {
type: 'remote' as 'remote' | 'local',
@@ -11,36 +33,61 @@ const default_config = {
},
subagent: {
enabled: true,
agents: {} as Record<string, v.InferInput<typeof agent_config_schema>>,
},
instructions: {
enabled: true,
},
skills: {
enabled: true,
enabled: true as boolean | string[],
},
};
export const config_schema = v.object({
mcp: v.optional(
v.object({
type: v.optional(v.picklist(['remote', 'local'])),
enabled: v.optional(v.boolean()),
}),
mcp: v.pipe(
v.optional(
v.object({
type: v.optional(v.picklist(['remote', 'local'])),
enabled: v.optional(v.boolean()),
}),
),
v.description(
"Configuration for the MCP. You can chose if it should be enabled or not and the transport to use 'remote' (default) and 'local'.",
),
),
subagent: v.optional(
v.object({
enabled: v.optional(v.boolean()),
}),
subagent: v.pipe(
v.optional(
v.object({
enabled: v.optional(v.boolean()),
agents: v.optional(v.record(v.string(), agent_config_schema)),
}),
),
v.description('Configuration for the subagent. You can choose if it should be enabled or not.'),
),
instructions: v.optional(
v.object({
enabled: v.optional(v.boolean()),
}),
instructions: v.pipe(
v.optional(
v.object({
enabled: v.optional(v.boolean()),
}),
),
v.description(
'Configuration for the automatic AGENTS.md injection. You can choose if it should be enabled or not.',
),
),
skills: v.optional(
v.object({
enabled: v.optional(v.boolean()),
}),
skills: v.pipe(
v.optional(
v.object({
enabled: v.pipe(
v.optional(v.union([v.boolean(), v.array(v.string())])),
v.description(
'It can be either a boolean or an array containing the skills that you want to enable',
),
),
}),
),
v.description(
'Configuration for the skills. You can choose if it they should be enabled or not, or specify an array of skill names to enable only specific skills.',
),
),
});
@@ -71,8 +118,15 @@ function get_config_paths() {
}
}
// returning config_dir first so it has higher priority
return [config_dir_path, global_path];
// Project-local: ./.opencode/svelte.json (cwd)
let project_path: string | null = null;
const project_config = join(process.cwd(), '.opencode', 'svelte.json');
if (existsSync(project_config)) {
project_path = project_config;
}
// Lowest priority first, highest priority last (project overrides global)
return [global_path, config_dir_path, project_path];
}
function load_config_file(config_path: string): ConfigLoadResult {
@@ -105,8 +159,12 @@ function merge_with_defaults(user_config: Partial<McpConfig>): McpConfig {
...user_config.mcp,
},
subagent: {
...default_config.subagent,
enabled: default_config.subagent.enabled,
...user_config.subagent,
agents: {
...default_config.subagent.agents,
...user_config.subagent?.agents,
},
},
instructions: {
...default_config.instructions,
@@ -121,6 +179,9 @@ function merge_with_defaults(user_config: Partial<McpConfig>): McpConfig {
export function get_mcp_config(ctx: PluginInput) {
const config_paths = get_config_paths();
let merged: Partial<McpConfig> = {};
// Iterate from lowest to highest priority, merging as we go
for (const path of config_paths) {
if (path && existsSync(path)) {
const result = load_config_file(path);
@@ -129,23 +190,32 @@ export function get_mcp_config(ctx: PluginInput) {
ctx.client.tui.showToast({
body: {
title: 'Svelte: Invalid opencode plugin config',
message: `${result.parse_error}\nUsing default values`,
message: `${result.parse_error} (${path})\nSkipping this config file`,
variant: 'warning',
duration: 7000,
},
});
}, 7000);
return default_config;
continue;
}
const parsed = v.safeParse(config_schema, result.data);
if (parsed.success) {
return merge_with_defaults(parsed.output);
merged = {
mcp: { ...merged.mcp, ...parsed.output.mcp },
subagent: {
...merged.subagent,
...parsed.output.subagent,
agents: { ...merged.subagent?.agents, ...parsed.output.subagent?.agents },
},
instructions: { ...merged.instructions, ...parsed.output.instructions },
skills: { ...merged.skills, ...parsed.output.skills },
};
} else {
setTimeout(() => {
ctx.client.tui.showToast({
body: {
title: 'Svelte: Invalid opencode plugin config',
message: `${result.parse_error}\nUsing default values`,
message: `Invalid config schema (${path})\nSkipping this config file`,
variant: 'warning',
duration: 7000,
},
@@ -155,5 +225,5 @@ export function get_mcp_config(ctx: PluginInput) {
}
}
return default_config;
return merge_with_defaults(merged);
}

View File

@@ -2,7 +2,8 @@ import type { Plugin } from '@opencode-ai/plugin';
import { readdir } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { get_mcp_config } from './config.js';
import { agents } from './agents.ts';
import { get_mcp_config } from './config.ts';
const current_dir = dirname(fileURLToPath(import.meta.url));
@@ -13,7 +14,9 @@ export const svelte_plugin: Plugin = async (ctx) => {
input.mcp ??= {};
input.instructions ??= [];
// @ts-expect-error -- types are wrong in the opencode package...will fix there and remove this
input.skills ??= [];
input.skills ??= {};
// @ts-expect-error -- types are wrong in the opencode package...will fix there and remove this
input.skills.paths ??= [];
// by default we use svelte as the name for the svelte MCP server
let svelte_mcp_name = 'svelte';
// we loop over every mcp server to see if any of them is already the svelte MCP server
@@ -37,43 +40,77 @@ export const svelte_plugin: Plugin = async (ctx) => {
input.instructions.push(...instructions_paths.map((file) => join(instructions_dir, file)));
}
if (mcp_config.skills?.enabled !== false) {
const skills_enabled = mcp_config.skills?.enabled;
if (skills_enabled !== false) {
const skills_dir = join(current_dir, 'skills');
// @ts-expect-error -- skills is a new opencode feature
input.skills.push(skills_dir);
if (Array.isArray(skills_enabled)) {
// only add specific skill directories by name
for (const skill_name of skills_enabled) {
const skill_path = join(skills_dir, skill_name);
// @ts-expect-error -- skills is a new opencode feature
input.skills.paths.push(skill_path);
}
} else {
// @ts-expect-error -- skills is a new opencode feature
input.skills.paths.push(skills_dir);
}
}
// if the user doesn't have the MCP server already we add one based on config
if (!input.mcp[svelte_mcp_name] && mcp_config.mcp?.enabled !== false) {
if (!input.mcp[svelte_mcp_name]) {
if (mcp_config.mcp?.type === 'remote') {
input.mcp[svelte_mcp_name] = {
type: 'remote',
url: 'https://mcp.svelte.dev/mcp',
enabled: mcp_config.mcp?.enabled ?? true,
};
} else {
input.mcp[svelte_mcp_name] = {
type: 'local',
command: ['npx', '-y', '@sveltejs/mcp'],
enabled: mcp_config.mcp?.enabled ?? true,
};
}
}
if (mcp_config.subagent?.enabled !== false) {
// we add the editor subagent that will be used when editing Svelte files to prevent wasting context on the main agent
input.agent['svelte-file-editor'] = {
color: '#ff3e00',
mode: 'subagent',
prompt: `You are a specialized Svelte coder. Always use the tools from the svelte MCP server to fetch documentation with \`get_documentation\` and validating the code with \`svelte_autofixer\`. If the autofixer returns any issue or suggestions solve them before summarizing the changes for the main agent.`,
description:
'Specialized Svelte 5 code editor. MUST BE USED PROACTIVELY when creating, editing, or reviewing any .svelte file or .svelte.ts/.svelte.js module and MUST use the tools from the MCP server. Fetches relevant documentation and validates code using the Svelte MCP server tools.',
permission: {
bash: 'ask',
edit: 'allow',
webfetch: 'ask',
},
tools: {
[`${svelte_mcp_name}_*`]: true,
},
};
for (const [agent_name, agent_data] of Object.entries(agents)) {
// we add the editor subagent that will be used when editing Svelte files to prevent wasting context on the main agent
const default_config: (typeof input.agent)[string] = {
color: '#ff3e00',
mode: 'subagent',
prompt: agent_data.prompt,
description: agent_data.description,
permission: {
bash: 'ask',
edit: 'allow',
webfetch: 'ask',
},
tools: {
[`${svelte_mcp_name}_*`]: true,
},
};
// Get per-agent config from svelte.json (if any)
const agent_config = mcp_config.subagent?.agents?.[agent_name];
// Configure agent from svelte.json only
// Priority: svelte.json agent config > defaults
input.agent[agent_name] = {
...default_config,
...(agent_config?.model !== undefined && {
model: agent_config.model,
}),
...(agent_config?.temperature !== undefined && {
temperature: agent_config.temperature,
}),
...(agent_config?.maxSteps !== undefined && {
maxSteps: agent_config.maxSteps,
}),
...(agent_config?.top_p !== undefined && {
top_p: agent_config.top_p,
}),
};
}
}
},
};

View File

@@ -1,6 +1,6 @@
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available MCP Tools:
## Available Svelte MCP Tools:
### 1. list-sections

View File

@@ -1,11 +1,11 @@
{
"name": "@sveltejs/opencode",
"version": "0.0.3",
"version": "0.1.8",
"type": "module",
"license": "MIT",
"homepage": "https://github.com/sveltejs/mcp#readme",
"homepage": "https://github.com/sveltejs/ai-tools#readme",
"bugs": {
"url": "https://github.com/sveltejs/mcp/issues"
"url": "https://github.com/sveltejs/ai-tools/issues"
},
"scripts": {
"check": "tsc --noEmit",
@@ -14,12 +14,19 @@
"files": [
"index.ts",
"config.ts",
"agents.ts",
"instructions",
"skills"
],
"exports": {
"./server": {
"types": "./index.ts",
"import": "./index.ts"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/sveltejs/mcp.git",
"url": "git+https://github.com/sveltejs/ai-tools.git",
"path": "packages/opencode"
},
"publishConfig": {

View File

@@ -14,16 +14,55 @@
"type": "boolean"
}
},
"required": []
"required": [],
"description": "Configuration for the MCP. You can chose if it should be enabled or not and the transport to use 'remote' (default) and 'local'."
},
"subagent": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"agents": {
"type": "object",
"propertyNames": {
"anyOf": [
{
"enum": [
"svelte-file-editor"
]
},
{
"type": "string"
}
]
},
"additionalProperties": {
"type": "object",
"properties": {
"model": {
"type": "string",
"description": "Model identifier for the agent (e.g., \"anthropic/claude-sonnet-4-20250514\")"
},
"temperature": {
"type": "number",
"description": "Temperature setting for the agent (e.g., 0.7)"
},
"top_p": {
"type": "number",
"description": "Control response diversity with the top_p option. Alternative to temperature for controlling randomness."
},
"maxSteps": {
"type": "number",
"description": "Maximum number of steps the agent can take (e.g., 10)"
}
},
"required": []
}
}
},
"required": []
"required": [],
"description": "Configuration for the subagent. You can choose if it should be enabled or not."
},
"instructions": {
"type": "object",
@@ -32,16 +71,39 @@
"type": "boolean"
}
},
"required": []
"required": [],
"description": "Configuration for the automatic AGENTS.md injection. You can choose if it should be enabled or not."
},
"skills": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
"anyOf": [
{
"type": "boolean"
},
{
"type": "array",
"items": {
"anyOf": [
{
"enum": [
"svelte-code-writer",
"svelte-core-bestpractices"
]
},
{
"type": "string"
}
]
}
}
],
"description": "It can be either a boolean or an array containing the skills that you want to enable"
}
},
"required": []
"required": [],
"description": "Configuration for the skills. You can choose if it they should be enabled or not, or specify an array of skill names to enable only specific skills."
}
},
"required": [],

View File

@@ -3,6 +3,58 @@ import { config_schema } from '../config.js';
import fs from 'node:fs';
import path from 'node:path';
const json_schema = toJsonSchema(config_schema);
// Read agent names from tools/agents/*.md files
function get_agent_names(agents_dir: string) {
if (!fs.existsSync(agents_dir)) return [];
return fs
.readdirSync(agents_dir, { withFileTypes: true })
.filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
.map((entry) => entry.name.replace(/\.md$/, ''));
}
function get_skill_names(skills_dir: string) {
if (!fs.existsSync(skills_dir)) return [];
return fs
.readdirSync(skills_dir, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name);
}
const skills_dir = path.resolve('./skills');
const skill_names = get_skill_names(skills_dir);
const schema = config_schema;
const json_schema = toJsonSchema(schema);
// Post-process: inject skill name suggestions into the items schema.
// This is the JSON Schema equivalent of `"a" | "b" | (string & {})` —
// editors will autocomplete the known names but any string is still valid.
if (skill_names.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const enabled = (json_schema as any).properties?.skills?.properties?.enabled;
if (enabled?.anyOf) {
const array_branch = enabled.anyOf.find((s: Record<string, unknown>) => s.type === 'array');
if (array_branch) {
array_branch.items = {
anyOf: [{ enum: skill_names }, { type: 'string' }],
};
}
}
}
// Post-process: inject known agent names for intellisense
// This is the JSON Schema equivalent of `"a" | "b" | (string & {})` —
// editors will autocomplete the known names but any string is still valid.
const agents_dir = path.resolve('../../tools/agents');
const agent_names = get_agent_names(agents_dir);
if (agent_names.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const agents = (json_schema as any).properties?.subagent?.properties?.agents;
if (agents) {
agents.propertyNames = {
anyOf: [{ enum: agent_names }, { type: 'string' }],
};
}
}
fs.writeFileSync(path.resolve('./schema.json'), JSON.stringify(json_schema, null, '\t'));

View File

@@ -1,6 +1,6 @@
---
name: svelte-code-writer
description: CLI tools for Svelte 5 documentation lookup and code analysis. MUST be used whenever creating or editing any Svelte component (.svelte) or Svelte module (.svelte.ts/.svelte.js). If possible, this skill should be executed within the svelte-file-editor agent for optimal results.
description: CLI tools for Svelte 5 documentation lookup and code analysis. MUST be used whenever creating, editing or analyzing any Svelte component (.svelte) or Svelte module (.svelte.ts/.svelte.js). If possible, this skill should be executed within the svelte-file-editor agent for optimal results.
---
# Svelte 5 Code Writer

View File

@@ -0,0 +1,176 @@
---
name: svelte-core-bestpractices
description: Guidance on writing fast, robust, modern Svelte code. Load this skill whenever in a Svelte project and asked to write/edit or analyze a Svelte component or module. Covers reactivity, event handling, styling, integration with libraries and more.
---
## `$state`
Only use the `$state` rune for variables that should be _reactive_ — in other words, variables that cause an `$effect`, `$derived` or template expression to update. Everything else can be a normal variable.
Objects and arrays (`$state({...})` or `$state([...])`) are made deeply reactive, meaning mutation will trigger updates. This has a trade-off: in exchange for fine-grained reactivity, the objects must be proxied, which has performance overhead. In cases where you're dealing with large objects that are only ever reassigned (rather than mutated), use `$state.raw` instead. This is often the case with API responses, for example.
## `$derived`
To compute something from state, use `$derived` rather than `$effect`:
```js
// do this
let square = $derived(num * num);
// don't do this
let square;
$effect(() => {
square = num * num;
});
```
> [!NOTE] `$derived` is given an expression, _not_ a function. If you need to use a function (because the expression is complex, for example) use `$derived.by`.
Deriveds are writable — you can assign to them, just like `$state`, except that they will re-evaluate when their expression changes.
If the derived expression is an object or array, it will be returned as-is — it is _not_ made deeply reactive. You can, however, use `$state` inside `$derived.by` in the rare cases that you need this.
## `$effect`
Effects are an escape hatch and should mostly be avoided. In particular, avoid updating state inside effects.
- If you need to sync state to an external library such as D3, it is often neater to use [`{@attach ...}`](references/@attach.md)
- If you need to run some code in response to user interaction, put the code directly in an event handler or use a [function binding](references/bind.md) as appropriate
- If you need to log values for debugging purposes, use [`$inspect`](references/$inspect.md)
- If you need to observe something external to Svelte, use [`createSubscriber`](references/svelte-reactivity.md)
Never wrap the contents of an effect in `if (browser) {...}` or similar — effects do not run on the server.
## `$props`
Treat props as though they will change. For example, values that depend on props should usually use `$derived`:
```js
// @errors: 2451
let { type } = $props();
// do this
let color = $derived(type === 'danger' ? 'red' : 'green');
// don't do this — `color` will not update if `type` changes
let color = type === 'danger' ? 'red' : 'green';
```
## `$inspect.trace`
`$inspect.trace` is a debugging tool for reactivity. If something is not updating properly or running more than it should you can add `$inspect.trace(label)` as the first line of an `$effect` or `$derived.by` (or any function they call) to trace their dependencies and discover which one triggered an update.
## Events
Any element attribute starting with `on` is treated as an event listener:
```svelte
<button onclick={() => {...}}>click me</button>
<!-- attribute shorthand also works -->
<button {onclick}>...</button>
<!-- so do spread attributes -->
<button {...props}>...</button>
```
If you need to attach listeners to `window` or `document` you can use `<svelte:window>` and `<svelte:document>`:
```svelte
<svelte:window onkeydown={...} />
<svelte:document onvisibilitychange={...} />
```
Avoid using `onMount` or `$effect` for this.
## Snippets
[Snippets](references/snippet.md) are a way to define reusable chunks of markup that can be instantiated with the [`{@render ...}`](references/@render.md) tag, or passed to components as props. They must be declared within the template.
```svelte
{#snippet greeting(name)}
<p>hello {name}!</p>
{/snippet}
{@render greeting('world')}
```
> [!NOTE] Snippets declared at the top level of a component (i.e. not inside elements or blocks) can be referenced inside `<script>`. A snippet that doesn't reference component state is also available in a `<script module>`, in which case it can be exported for use by other components.
## Each blocks
Prefer to use [keyed each blocks](references/each.md) — this improves performance by allowing Svelte to surgically insert or remove items rather than updating the DOM belonging to existing items.
> [!NOTE] The key _must_ uniquely identify the object. Do not use the index as a key.
Avoid destructuring if you need to mutate the item (with something like `bind:value={item.count}`, for example).
## Using JavaScript variables in CSS
If you have a JS variable that you want to use inside CSS you can set a CSS custom property with the `style:` directive.
```svelte
<div style:--columns={columns}>...</div>
```
You can then reference `var(--columns)` inside the component's `<style>`.
## Styling child components
The CSS in a component's `<style>` is scoped to that component. If a parent component needs to control the child's styles, the preferred way is to use CSS custom properties:
```svelte
<!-- Parent.svelte -->
<Child --color="red" />
<!-- Child.svelte -->
<h1>Hello</h1>
<style>
h1 {
color: var(--color);
}
</style>
```
If this is impossible (for example, the child component comes from a library) you can use `:global` to override styles:
```svelte
<div>
<Child />
</div>
<style>
div :global {
h1 {
color: red;
}
}
</style>
```
## Context
Consider using context instead of declaring state in a shared module. This will scope the state to the part of the app that needs it, and eliminate the possibility of it leaking between users when server-side rendering.
Use `createContext` rather than `setContext` and `getContext`, as it provides type safety.
## Async Svelte
If using version 5.36 or higher, you can use [await expressions](references/await-expressions.md) and [hydratable](references/hydratable.md) to use promises directly inside components. Note that these require the `experimental.async` option to be enabled in `svelte.config.js` as they are not yet considered fully stable.
## Avoid legacy features
Always use runes mode for new code, and avoid features that have more modern replacements:
- use `$state` instead of implicit reactivity (e.g. `let count = 0; count += 1`)
- use `$derived` and `$effect` instead of `$:` assignments and statements (but only use effects when there is no better solution)
- use `$props` instead of `export let`, `$$props` and `$$restProps`
- use `onclick={...}` instead of `on:click={...}`
- use `{#snippet ...}` and `{@render ...}` instead of `<slot>` and `$$slots` and `<svelte:fragment>`
- use `<DynamicComponent>` instead of `<svelte:component this={DynamicComponent}>`
- use `import Self from './ThisComponent.svelte'` and `<Self>` instead of `<svelte:self>`
- use classes with `$state` fields to share reactivity between components, instead of using stores
- use `{@attach ...}` instead of `use:action`
- use clsx-style arrays and objects in `class` attributes, instead of the `class:` directive

View File

@@ -0,0 +1,53 @@
> [!NOTE] `$inspect` only works during development. In a production build it becomes a noop.
The `$inspect` rune is roughly equivalent to `console.log`, with the exception that it will re-run whenever its argument changes. `$inspect` tracks reactive state deeply, meaning that updating something inside an object or array using fine-grained reactivity will cause it to re-fire (demo:
```svelte
<script>
let count = $state(0);
let message = $state('hello');
$inspect(count, message); // will console.log when `count` or `message` change
</script>
<button onclick={() => count++}>Increment</button>
<input bind:value={message} />
```
On updates, a stack trace will be printed, making it easy to find the origin of a state change (unless you're in the playground, due to technical limitations).
## $inspect(...).with
`$inspect` returns a property `with`, which you can invoke with a callback, which will then be invoked instead of `console.log`. The first argument to the callback is either `"init"` or `"update"`; subsequent arguments are the values passed to `$inspect` (demo:
```svelte
<script>
let count = $state(0);
$inspect(count).with((type, count) => {
if (type === 'update') {
debugger; // or `console.trace`, or whatever you want
}
});
</script>
<button onclick={() => count++}>Increment</button>
```
## $inspect.trace(...)
This rune, added in 5.14, causes the surrounding function to be _traced_ in development. Any time the function re-runs as part of an [effect]($effect) or a [derived]($derived), information will be printed to the console about which pieces of reactive state caused the effect to fire.
```svelte
<script>
import { doSomeWork } from './elsewhere';
$effect(() => {
+++// $inspect.trace must be the first statement of a function body+++
+++$inspect.trace();+++
doSomeWork();
});
</script>
```
`$inspect.trace` takes an optional first argument which will be used as the label.

View File

@@ -0,0 +1,166 @@
Attachments are functions that run in an [effect]($effect) when an element is mounted to the DOM or when [state]($state) read inside the function updates.
Optionally, they can return a function that is called before the attachment re-runs, or after the element is later removed from the DOM.
> [!NOTE]
> Attachments are available in Svelte 5.29 and newer.
```svelte
<!--- file: App.svelte --->
<script>
/** @type {import('svelte/attachments').Attachment} */
function myAttachment(element) {
console.log(element.nodeName); // 'DIV'
return () => {
console.log('cleaning up');
};
}
</script>
<div {@attach myAttachment}>...</div>
```
An element can have any number of attachments.
## Attachment factories
A useful pattern is for a function, such as `tooltip` in this example, to _return_ an attachment (demo:
```svelte
<!--- file: App.svelte --->
<script>
import tippy from 'tippy.js';
let content = $state('Hello!');
/**
* @param {string} content
* @returns {import('svelte/attachments').Attachment}
*/
function tooltip(content) {
return (element) => {
const tooltip = tippy(element, { content });
return tooltip.destroy;
};
}
</script>
<input bind:value={content} />
<button {@attach tooltip(content)}> Hover me </button>
```
Since the `tooltip(content)` expression runs inside an [effect]($effect), the attachment will be destroyed and recreated whenever `content` changes. The same thing would happen for any state read _inside_ the attachment function when it first runs. (If this isn't what you want, see [Controlling when attachments re-run](#Controlling-when-attachments-re-run).)
## Inline attachments
Attachments can also be created inline (demo:
```svelte
<!--- file: App.svelte --->
<canvas
width={32}
height={32}
{@attach (canvas) => {
const context = canvas.getContext('2d');
$effect(() => {
context.fillStyle = color;
context.fillRect(0, 0, canvas.width, canvas.height);
});
}}
></canvas>
```
> [!NOTE]
> The nested effect runs whenever `color` changes, while the outer effect (where `canvas.getContext(...)` is called) only runs once, since it doesn't read any reactive state.
## Conditional attachments
Falsy values like `false` or `undefined` are treated as no attachment, enabling conditional usage:
```svelte
<div {@attach enabled && myAttachment}>...</div>
```
## Passing attachments to components
When used on a component, `{@attach ...}` will create a prop whose key is a [`Symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). If the component then [spreads](/tutorial/svelte/spread-props) props onto an element, the element will receive those attachments.
This allows you to create _wrapper components_ that augment elements (demo:
```svelte
<!--- file: Button.svelte --->
<script>
/** @type {import('svelte/elements').HTMLButtonAttributes} */
let { children, ...props } = $props();
</script>
<!-- `props` includes attachments -->
<button {...props}>
{@render children?.()}
</button>
```
```svelte
<!--- file: App.svelte --->
<script>
import tippy from 'tippy.js';
import Button from './Button.svelte';
let content = $state('Hello!');
/**
* @param {string} content
* @returns {import('svelte/attachments').Attachment}
*/
function tooltip(content) {
return (element) => {
const tooltip = tippy(element, { content });
return tooltip.destroy;
};
}
</script>
<input bind:value={content} />
<Button {@attach tooltip(content)}>Hover me</Button>
```
## Controlling when attachments re-run
Attachments, unlike [actions](use), are fully reactive: `{@attach foo(bar)}` will re-run on changes to `foo` _or_ `bar` (or any state read inside `foo`):
```js
// @errors: 7006 2304 2552
function foo(bar) {
return (node) => {
veryExpensiveSetupWork(node);
update(node, bar);
};
}
```
In the rare case that this is a problem (for example, if `foo` does expensive and unavoidable setup work) consider passing the data inside a function and reading it in a child effect:
```js
// @errors: 7006 2304 2552
function foo(+++getBar+++) {
return (node) => {
veryExpensiveSetupWork(node);
+++ $effect(() => {
update(node, getBar());
});+++
}
}
```
## Creating attachments programmatically
To add attachments to an object that will be spread onto a component or element, use [`createAttachmentKey`](svelte-attachments#createAttachmentKey).
## Converting actions to attachments
If you're using a library that only provides actions, you can convert them to attachments with [`fromAction`](svelte-attachments#fromAction), allowing you to (for example) use them with components.

View File

@@ -0,0 +1,35 @@
To render a [snippet](snippet), use a `{@render ...}` tag.
```svelte
{#snippet sum(a, b)}
<p>{a} + {b} = {a + b}</p>
{/snippet}
{@render sum(1, 2)}
{@render sum(3, 4)}
{@render sum(5, 6)}
```
The expression can be an identifier like `sum`, or an arbitrary JavaScript expression:
```svelte
{@render (cool ? coolSnippet : lameSnippet)()}
```
## Optional snippets
If the snippet is potentially undefined — for example, because it's an incoming prop — then you can use optional chaining to only render it when it _is_ defined:
```svelte
{@render children?.()}
```
Alternatively, use an [`{#if ...}`](if) block with an `:else` clause to render fallback content:
```svelte
{#if children}
{@render children()}
{:else}
<p>fallback content</p>
{/if}
```

View File

@@ -0,0 +1,180 @@
As of Svelte 5.36, you can use the `await` keyword inside your components in three places where it was previously unavailable:
- at the top level of your component's `<script>`
- inside `$derived(...)` declarations
- inside your markup
This feature is currently experimental, and you must opt in by adding the `experimental.async` option wherever you [configure](/docs/kit/configuration) Svelte, usually `svelte.config.js`:
```js
/// file: svelte.config.js
export default {
compilerOptions: {
experimental: {
async: true,
},
},
};
```
The experimental flag will be removed in Svelte 6.
## Synchronized updates
When an `await` expression depends on a particular piece of state, changes to that state will not be reflected in the UI until the asynchronous work has completed, so that the UI is not left in an inconsistent state. In other words, in an example like this...
```svelte
<script>
let a = $state(1);
let b = $state(2);
async function add(a, b) {
await new Promise((f) => setTimeout(f, 500)); // artificial delay
return a + b;
}
</script>
<input type="number" bind:value={a} />
<input type="number" bind:value={b} />
<p>{a} + {b} = {await add(a, b)}</p>
```
...if you increment `a`, the contents of the `<p>` will _not_ immediately update to read this —
```html
<p>2 + 2 = 3</p>
```
— instead, the text will update to `2 + 2 = 4` when `add(a, b)` resolves.
Updates can overlap — a fast update will be reflected in the UI while an earlier slow update is still ongoing.
## Concurrency
Svelte will do as much asynchronous work as it can in parallel. For example if you have two `await` expressions in your markup...
```svelte
<p>{await one()}</p><p>{await two()}</p>
```
...both functions will run at the same time, as they are independent expressions, even though they are _visually_ sequential.
This does not apply to sequential `await` expressions inside your `<script>` or inside async functions — these run like any other asynchronous JavaScript. An exception is that independent `$derived` expressions will update independently, even though they will run sequentially when they are first created:
```js
// these will run sequentially the first time,
// but will update independently
let a = $derived(await one());
let b = $derived(await two());
```
> [!NOTE] If you write code like this, expect Svelte to give you an [`await_waterfall`](runtime-warnings#Client-warnings-await_waterfall) warning
## Indicating loading states
To render placeholder UI, you can wrap content in a `<svelte:boundary>` with a [`pending`](svelte-boundary#Properties-pending) snippet. This will be shown when the boundary is first created, but not for subsequent updates, which are globally coordinated.
After the contents of a boundary have resolved for the first time and have replaced the `pending` snippet, you can detect subsequent async work with [`$effect.pending()`]($effect#$effect.pending). This is what you would use to display a "we're asynchronously validating your input" spinner next to a form field, for example.
You can also use [`settled()`](svelte#settled) to get a promise that resolves when the current update is complete:
```js
import { tick, settled } from 'svelte';
async function onclick() {
updating = true;
// without this, the change to `updating` will be
// grouped with the other changes, meaning it
// won't be reflected in the UI
await tick();
color = 'octarine';
answer = 42;
await settled();
// any updates affected by `color` or `answer`
// have now been applied
updating = false;
}
```
## Error handling
Errors in `await` expressions will bubble to the nearest [error boundary](svelte-boundary).
## Server-side rendering
Svelte supports asynchronous server-side rendering (SSR) with the `render(...)` API. To use it, simply await the return value:
```js
/// file: server.js
import { render } from 'svelte/server';
import App from './App.svelte';
const { head, body } = +++await+++ render(App);
```
> [!NOTE] If you're using a framework like SvelteKit, this is done on your behalf.
If a `<svelte:boundary>` with a `pending` snippet is encountered during SSR, that snippet will be rendered while the rest of the content is ignored. All `await` expressions encountered outside boundaries with `pending` snippets will resolve and render their contents prior to `await render(...)` returning.
> [!NOTE] In the future, we plan to add a streaming implementation that renders the content in the background.
## Forking
The [`fork(...)`](svelte#fork) API, added in 5.42, makes it possible to run `await` expressions that you _expect_ to happen in the near future. This is mainly intended for frameworks like SvelteKit to implement preloading when (for example) users signal an intent to navigate.
```svelte
<script>
import { fork } from 'svelte';
import Menu from './Menu.svelte';
let open = $state(false);
/** @type {import('svelte').Fork | null} */
let pending = null;
function preload() {
pending ??= fork(() => {
open = true;
});
}
function discard() {
pending?.discard();
pending = null;
}
</script>
<button
onfocusin={preload}
onfocusout={discard}
onpointerenter={preload}
onpointerleave={discard}
onclick={() => {
pending?.commit();
pending = null;
// in case `pending` didn't exist
// (if it did, this is a no-op)
open = true;
}}>open menu</button
>
{#if open}
<!-- any async work inside this component will start
as soon as the fork is created -->
<Menu onclose={() => (open = false)} />
{/if}
```
## Caveats
As an experimental feature, the details of how `await` is handled (and related APIs like `$effect.pending()`) are subject to breaking changes outside of a semver major release, though we intend to keep such changes to a bare minimum.
## Breaking changes
Effects run in a slightly different order when the `experimental.async` option is `true`. Specifically, _block_ effects like `{#if ...}` and `{#each ...}` now run before an `$effect.pre` or `beforeUpdate` in the same component, which means that in very rare situations.

View File

@@ -0,0 +1,16 @@
## Function bindings
You can also use `bind:property={get, set}`, where `get` and `set` are functions, allowing you to perform validation and transformation:
```svelte
<input bind:value={() => value, (v) => (value = v.toLowerCase())} />
```
In the case of readonly bindings like [dimension bindings](#Dimensions), the `get` value should be `null`:
```svelte
<div bind:clientWidth={null, redraw} bind:clientHeight={null, redraw}>...</div>
```
> [!NOTE]
> Function bindings are available in Svelte 5.9.0 and newer.

View File

@@ -0,0 +1,42 @@
## Keyed each blocks
```svelte
<!--- copy: false --->
{#each expression as name (key)}...{/each}
```
```svelte
<!--- copy: false --->
{#each expression as name, index (key)}...{/each}
```
If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to intelligently update the list when data changes by inserting, moving and deleting items, rather than adding or removing items at the end and updating the state in the middle.
The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change.
```svelte
{#each items as item (item.id)}
<li>{item.name} x {item.qty}</li>
{/each}
<!-- or with additional index value -->
{#each items as item, i (item.id)}
<li>{i + 1}: {item.name} x {item.qty}</li>
{/each}
```
You can freely use destructuring and rest patterns in each blocks.
```svelte
{#each items as { id, name, qty }, i (id)}
<li>{i + 1}: {name} x {qty}</li>
{/each}
{#each objects as { id, ...rest }}
<li><span>{id}</span><MyComponent {...rest} /></li>
{/each}
{#each items as [id, ...rest]}
<li><span>{id}</span><MyComponent values={rest} /></li>
{/each}
```

View File

@@ -0,0 +1,100 @@
In Svelte, when you want to render asynchronous content data on the server, you can simply `await` it. This is great! However, it comes with a pitfall: when hydrating that content on the client, Svelte has to redo the asynchronous work, which blocks hydration for however long it takes:
```svelte
<script>
import { getUser } from 'my-database-library';
// This will get the user on the server, render the user's name into the h1,
// and then, during hydration on the client, it will get the user _again_,
// blocking hydration until it's done.
const user = await getUser();
</script>
<h1>{user.name}</h1>
```
That's silly, though. If we've already done the hard work of getting the data on the server, we don't want to get it again during hydration on the client. `hydratable` is a low-level API built to solve this problem. You probably won't need this very often — it will be used behind the scenes by whatever datafetching library you use. For example, it powers [remote functions in SvelteKit](/docs/kit/remote-functions).
To fix the example above:
```svelte
<script>
import { hydratable } from 'svelte';
import { getUser } from 'my-database-library';
// During server rendering, this will serialize and stash the result of `getUser`, associating
// it with the provided key and baking it into the `head` content. During hydration, it will
// look for the serialized version, returning it instead of running `getUser`. After hydration
// is done, if it's called again, it'll simply invoke `getUser`.
const user = await hydratable('user', () => getUser());
</script>
<h1>{user.name}</h1>
```
This API can also be used to provide access to random or time-based values that are stable between server rendering and hydration. For example, to get a random number that doesn't update on hydration:
```ts
import { hydratable } from 'svelte';
const rand = hydratable('random', () => Math.random());
```
If you're a library author, be sure to prefix the keys of your `hydratable` values with the name of your library so that your keys don't conflict with other libraries.
## Serialization
All data returned from a `hydratable` function must be serializable. But this doesn't mean you're limited to JSON — Svelte uses [`devalue`](https://npmjs.com/package/devalue), which can serialize all sorts of things including `Map`, `Set`, `URL`, and `BigInt`. Check the documentation page for a full list. In addition to these, thanks to some Svelte magic, you can also fearlessly use promises:
```svelte
<script>
import { hydratable } from 'svelte';
const promises = hydratable('random', () => {
return {
one: Promise.resolve(1),
two: Promise.resolve(2),
};
});
</script>
{await promises.one}
{await promises.two}
```
## CSP
`hydratable` adds an inline `<script>` block to the `head` returned from `render`. If you're using [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) (CSP), this script will likely fail to run. You can provide a `nonce` to `render`:
```js
const nonce = crypto.randomUUID();
const { head, body } = await render(App, {
csp: { nonce },
});
```
This will add the `nonce` to the script block, on the assumption that you will later add the same nonce to the CSP header of the document that contains it:
```js
response.headers.set('Content-Security-Policy', `script-src 'nonce-${nonce}'`);
```
It's essential that a `nonce` — which, British slang definition aside, means 'number used once' — is only used when dynamically server rendering an individual response.
If instead you are generating static HTML ahead of time, you must use hashes instead:
```js
const { head, body, hashes } = await render(App, {
csp: { hash: true },
});
```
`hashes.script` will be an array of strings like `["sha256-abcd123"]`. As with `nonce`, the hashes should be used in your CSP header:
```js
response.headers.set(
'Content-Security-Policy',
`script-src ${hashes.script.map((hash) => `'${hash}'`).join(' ')}`,
);
```
We recommend using `nonce` over hash if you can, as `hash` will interfere with streaming SSR in the future.

View File

@@ -0,0 +1,276 @@
```svelte
<!--- copy: false --->
{#snippet name()}...{/snippet}
```
```svelte
<!--- copy: false --->
{#snippet name(param1, param2, paramN)}...{/snippet}
```
Snippets, and [render tags](@render), are a way to create reusable chunks of markup inside your components. Instead of writing duplicative code like this...
```svelte
{#each images as image}
{#if image.href}
<a href={image.href}>
<figure>
<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
<figcaption>{image.caption}</figcaption>
</figure>
</a>
{:else}
<figure>
<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
<figcaption>{image.caption}</figcaption>
</figure>
{/if}
{/each}
```
...you can write this:
```svelte
{#snippet figure(image)}
<figure>
<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
<figcaption>{image.caption}</figcaption>
</figure>
{/snippet}
{#each images as image}
{#if image.href}
<a href={image.href}>
{@render figure(image)}
</a>
{:else}
{@render figure(image)}
{/if}
{/each}
```
Like function declarations, snippets can have an arbitrary number of parameters, which can have default values, and you can destructure each parameter. You cannot use rest parameters, however.
## Snippet scope
Snippets can be declared anywhere inside your component. They can reference values declared outside themselves, for example in the `<script>` tag or in `{#each ...}` blocks (demo...
```svelte
<script>
let { message = `it's great to see you!` } = $props();
</script>
{#snippet hello(name)}
<p>hello {name}! {message}!</p>
{/snippet}
{@render hello('alice')}
{@render hello('bob')}
```
...and they are 'visible' to everything in the same lexical scope (i.e. siblings, and children of those siblings):
```svelte
<div>
{#snippet x()}
{#snippet y()}...{/snippet}
<!-- this is fine -->
{@render y()}
{/snippet}
<!-- this will error, as `y` is not in scope -->
{@render y()}
</div>
<!-- this will also error, as `x` is not in scope -->
{@render x()}
```
Snippets can reference themselves and each other (demo:
```svelte
{#snippet blastoff()}
<span>🚀</span>
{/snippet}
{#snippet countdown(n)}
{#if n > 0}
<span>{n}...</span>
{@render countdown(n - 1)}
{:else}
{@render blastoff()}
{/if}
{/snippet}
{@render countdown(10)}
```
## Passing snippets to components
### Explicit props
Within the template, snippets are values just like any other. As such, they can be passed to components as props (demo:
```svelte
<script>
import Table from './Table.svelte';
const fruits = [
{ name: 'apples', qty: 5, price: 2 },
{ name: 'bananas', qty: 10, price: 1 },
{ name: 'cherries', qty: 20, price: 0.5 },
];
</script>
{#snippet header()}
<th>fruit</th>
<th>qty</th>
<th>price</th>
<th>total</th>
{/snippet}
{#snippet row(d)}
<td>{d.name}</td>
<td>{d.qty}</td>
<td>{d.price}</td>
<td>{d.qty * d.price}</td>
{/snippet}
<Table data={fruits} {header} {row} />
```
Think about it like passing content instead of data to a component. The concept is similar to slots in web components.
### Implicit props
As an authoring convenience, snippets declared directly _inside_ a component implicitly become props _on_ the component (demo:
```svelte
<!-- this is semantically the same as the above -->
<Table data={fruits}>
{#snippet header()}
<th>fruit</th>
<th>qty</th>
<th>price</th>
<th>total</th>
{/snippet}
{#snippet row(d)}
<td>{d.name}</td>
<td>{d.qty}</td>
<td>{d.price}</td>
<td>{d.qty * d.price}</td>
{/snippet}
</Table>
```
### Implicit `children` snippet
Any content inside the component tags that is _not_ a snippet declaration implicitly becomes part of the `children` snippet (demo:
```svelte
<!--- file: App.svelte --->
<Button>click me</Button>
```
```svelte
<!--- file: Button.svelte --->
<script>
let { children } = $props();
</script>
<!-- result will be <button>click me</button> -->
<button>{@render children()}</button>
```
> [!NOTE] Note that you cannot have a prop called `children` if you also have content inside the component — for this reason, you should avoid having props with that name
### Optional snippet props
You can declare snippet props as being optional. You can either use optional chaining to not render anything if the snippet isn't set...
```svelte
<script>
let { children } = $props();
</script>
{@render children?.()}
```
...or use an `#if` block to render fallback content:
```svelte
<script>
let { children } = $props();
</script>
{#if children}
{@render children()}
{:else}
fallback content
{/if}
```
## Typing snippets
Snippets implement the `Snippet` interface imported from `'svelte'`:
```svelte
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
data: any[];
children: Snippet;
row: Snippet<[any]>;
}
let { data, children, row }: Props = $props();
</script>
```
With this change, red squigglies will appear if you try and use the component without providing a `data` prop and a `row` snippet. Notice that the type argument provided to `Snippet` is a tuple, since snippets can have multiple parameters.
We can tighten things up further by declaring a generic, so that `data` and `row` refer to the same type:
```svelte
<script lang="ts" generics="T">
import type { Snippet } from 'svelte';
let {
data,
children,
row,
}: {
data: T[];
children: Snippet;
row: Snippet<[T]>;
} = $props();
</script>
```
## Exporting snippets
Snippets declared at the top level of a `.svelte` file can be exported from a `<script module>` for use in other components, provided they don't reference any declarations in a non-module `<script>` (whether directly or indirectly, via other snippets) (demo:
```svelte
<script module>
export { add };
</script>
{#snippet add(a, b)}
{a} + {b} = {a + b}
{/snippet}
```
> [!NOTE]
> This requires Svelte 5.5.0 or newer
## Programmatic snippets
Snippets can be created programmatically with the [`createRawSnippet`](svelte#createRawSnippet) API. This is intended for advanced use cases.
## Snippets and slots
In Svelte 4, content can be passed to components using [slots](legacy-slots). Snippets are more powerful and flexible, and so slots have been deprecated in Svelte 5.

View File

@@ -0,0 +1,61 @@
## createSubscriber
<blockquote class="since note">
Available since 5.7.0
</blockquote>
Returns a `subscribe` function that integrates external event-based systems with Svelte's reactivity.
It's particularly useful for integrating with web APIs like `MediaQuery`, `IntersectionObserver`, or `WebSocket`.
If `subscribe` is called inside an effect (including indirectly, for example inside a getter),
the `start` callback will be called with an `update` function. Whenever `update` is called, the effect re-runs.
If `start` returns a cleanup function, it will be called when the effect is destroyed.
If `subscribe` is called in multiple effects, `start` will only be called once as long as the effects
are active, and the returned teardown function will only be called when all effects are destroyed.
It's best understood with an example. Here's an implementation of [`MediaQuery`](/docs/svelte/svelte-reactivity#MediaQuery):
```js
// @errors: 7031
import { createSubscriber } from 'svelte/reactivity';
import { on } from 'svelte/events';
export class MediaQuery {
#query;
#subscribe;
constructor(query) {
this.#query = window.matchMedia(`(${query})`);
this.#subscribe = createSubscriber((update) => {
// when the `change` event occurs, re-run any effects that read `this.current`
const off = on(this.#query, 'change', update);
// stop listening when all the effects are destroyed
return () => off();
});
}
get current() {
// This makes the getter reactive, if read in an effect
this.#subscribe();
// Return the current state of the query, whether or not we're in an effect
return this.#query.matches;
}
}
```
<div class="ts-block">
```dts
function createSubscriber(
start: (update: () => void) => (() => void) | void
): () => void;
```
</div>

View File

@@ -1,5 +1,9 @@
{
"extends": "../../tsconfig.json",
"include": ["index.ts", "config.ts", "scripts/*"],
"compilerOptions": {
"allowImportingTsExtensions": true,
"types": ["@types/node"]
},
"include": ["index.ts", "config.ts", "agents.ts", "scripts/*"],
"exclude": ["node_modules"]
}

View File

@@ -1,13 +1,13 @@
{
"name": "svelte",
"description": "A plugin for all things related to Svelte development, MCP, skills, and more.",
"version": "1.0.0",
"version": "1.0.4",
"author": {
"name": "Svelte"
},
"lspServers": {
"svelte": {
"command": "svelte-language-server",
"command": "svelteserver",
"args": ["--stdio"],
"extensionToLanguage": {
".svelte": "svelte"

View File

@@ -1,11 +1,15 @@
---
name: svelte-file-editor
description: Specialized Svelte 5 code editor. MUST BE USED PROACTIVELY when creating, editing, or reviewing any .svelte file or .svelte.ts/.svelte.js module and MUST use the tools from the MCP server. Fetches relevant documentation and validates code using the Svelte MCP server tools.
description: Specialized Svelte 5 code editor. MUST BE USED PROACTIVELY when creating, editing, or reviewing any .svelte file or .svelte.ts/.svelte.js module and MUST use the tools from the MCP server or the `svelte-file-editor` skill if they are available. Fetches relevant documentation and validates code using the Svelte MCP server tools.
permissionMode: acceptEdits
---
You are a Svelte 5 expert responsible for writing, editing, and validating Svelte components and modules. You have access to the Svelte MCP server which provides documentation and code analysis tools. Always use the tools from the svelte MCP server to fetch documentation with `get_documentation` and validating the code with `svelte_autofixer`. If the autofixer returns any issue or suggestions try to solve them.
If the MCP tools are not available you can use the `svelte-code-writer` skill to learn how to use the `@sveltejs/mcp` cli to access the same tools.
If the skill is not available you can run `npx @sveltejs/mcp@latest -y --help` to learn how to use it.
## Available MCP Tools
### 1. list-sections

View File

@@ -1,6 +1,6 @@
---
name: svelte-code-writer
description: CLI tools for Svelte 5 documentation lookup and code analysis. MUST be used whenever creating or editing any Svelte component (.svelte) or Svelte module (.svelte.ts/.svelte.js). If possible, this skill should be executed within the svelte-file-editor agent for optimal results.
description: CLI tools for Svelte 5 documentation lookup and code analysis. MUST be used whenever creating, editing or analyzing any Svelte component (.svelte) or Svelte module (.svelte.ts/.svelte.js). If possible, this skill should be executed within the svelte-file-editor agent for optimal results.
---
# Svelte 5 Code Writer

View File

@@ -0,0 +1,176 @@
---
name: svelte-core-bestpractices
description: Guidance on writing fast, robust, modern Svelte code. Load this skill whenever in a Svelte project and asked to write/edit or analyze a Svelte component or module. Covers reactivity, event handling, styling, integration with libraries and more.
---
## `$state`
Only use the `$state` rune for variables that should be _reactive_ — in other words, variables that cause an `$effect`, `$derived` or template expression to update. Everything else can be a normal variable.
Objects and arrays (`$state({...})` or `$state([...])`) are made deeply reactive, meaning mutation will trigger updates. This has a trade-off: in exchange for fine-grained reactivity, the objects must be proxied, which has performance overhead. In cases where you're dealing with large objects that are only ever reassigned (rather than mutated), use `$state.raw` instead. This is often the case with API responses, for example.
## `$derived`
To compute something from state, use `$derived` rather than `$effect`:
```js
// do this
let square = $derived(num * num);
// don't do this
let square;
$effect(() => {
square = num * num;
});
```
> [!NOTE] `$derived` is given an expression, _not_ a function. If you need to use a function (because the expression is complex, for example) use `$derived.by`.
Deriveds are writable — you can assign to them, just like `$state`, except that they will re-evaluate when their expression changes.
If the derived expression is an object or array, it will be returned as-is — it is _not_ made deeply reactive. You can, however, use `$state` inside `$derived.by` in the rare cases that you need this.
## `$effect`
Effects are an escape hatch and should mostly be avoided. In particular, avoid updating state inside effects.
- If you need to sync state to an external library such as D3, it is often neater to use [`{@attach ...}`](references/@attach.md)
- If you need to run some code in response to user interaction, put the code directly in an event handler or use a [function binding](references/bind.md) as appropriate
- If you need to log values for debugging purposes, use [`$inspect`](references/$inspect.md)
- If you need to observe something external to Svelte, use [`createSubscriber`](references/svelte-reactivity.md)
Never wrap the contents of an effect in `if (browser) {...}` or similar — effects do not run on the server.
## `$props`
Treat props as though they will change. For example, values that depend on props should usually use `$derived`:
```js
// @errors: 2451
let { type } = $props();
// do this
let color = $derived(type === 'danger' ? 'red' : 'green');
// don't do this — `color` will not update if `type` changes
let color = type === 'danger' ? 'red' : 'green';
```
## `$inspect.trace`
`$inspect.trace` is a debugging tool for reactivity. If something is not updating properly or running more than it should you can add `$inspect.trace(label)` as the first line of an `$effect` or `$derived.by` (or any function they call) to trace their dependencies and discover which one triggered an update.
## Events
Any element attribute starting with `on` is treated as an event listener:
```svelte
<button onclick={() => {...}}>click me</button>
<!-- attribute shorthand also works -->
<button {onclick}>...</button>
<!-- so do spread attributes -->
<button {...props}>...</button>
```
If you need to attach listeners to `window` or `document` you can use `<svelte:window>` and `<svelte:document>`:
```svelte
<svelte:window onkeydown={...} />
<svelte:document onvisibilitychange={...} />
```
Avoid using `onMount` or `$effect` for this.
## Snippets
[Snippets](references/snippet.md) are a way to define reusable chunks of markup that can be instantiated with the [`{@render ...}`](references/@render.md) tag, or passed to components as props. They must be declared within the template.
```svelte
{#snippet greeting(name)}
<p>hello {name}!</p>
{/snippet}
{@render greeting('world')}
```
> [!NOTE] Snippets declared at the top level of a component (i.e. not inside elements or blocks) can be referenced inside `<script>`. A snippet that doesn't reference component state is also available in a `<script module>`, in which case it can be exported for use by other components.
## Each blocks
Prefer to use [keyed each blocks](references/each.md) — this improves performance by allowing Svelte to surgically insert or remove items rather than updating the DOM belonging to existing items.
> [!NOTE] The key _must_ uniquely identify the object. Do not use the index as a key.
Avoid destructuring if you need to mutate the item (with something like `bind:value={item.count}`, for example).
## Using JavaScript variables in CSS
If you have a JS variable that you want to use inside CSS you can set a CSS custom property with the `style:` directive.
```svelte
<div style:--columns={columns}>...</div>
```
You can then reference `var(--columns)` inside the component's `<style>`.
## Styling child components
The CSS in a component's `<style>` is scoped to that component. If a parent component needs to control the child's styles, the preferred way is to use CSS custom properties:
```svelte
<!-- Parent.svelte -->
<Child --color="red" />
<!-- Child.svelte -->
<h1>Hello</h1>
<style>
h1 {
color: var(--color);
}
</style>
```
If this is impossible (for example, the child component comes from a library) you can use `:global` to override styles:
```svelte
<div>
<Child />
</div>
<style>
div :global {
h1 {
color: red;
}
}
</style>
```
## Context
Consider using context instead of declaring state in a shared module. This will scope the state to the part of the app that needs it, and eliminate the possibility of it leaking between users when server-side rendering.
Use `createContext` rather than `setContext` and `getContext`, as it provides type safety.
## Async Svelte
If using version 5.36 or higher, you can use [await expressions](references/await-expressions.md) and [hydratable](references/hydratable.md) to use promises directly inside components. Note that these require the `experimental.async` option to be enabled in `svelte.config.js` as they are not yet considered fully stable.
## Avoid legacy features
Always use runes mode for new code, and avoid features that have more modern replacements:
- use `$state` instead of implicit reactivity (e.g. `let count = 0; count += 1`)
- use `$derived` and `$effect` instead of `$:` assignments and statements (but only use effects when there is no better solution)
- use `$props` instead of `export let`, `$$props` and `$$restProps`
- use `onclick={...}` instead of `on:click={...}`
- use `{#snippet ...}` and `{@render ...}` instead of `<slot>` and `$$slots` and `<svelte:fragment>`
- use `<DynamicComponent>` instead of `<svelte:component this={DynamicComponent}>`
- use `import Self from './ThisComponent.svelte'` and `<Self>` instead of `<svelte:self>`
- use classes with `$state` fields to share reactivity between components, instead of using stores
- use `{@attach ...}` instead of `use:action`
- use clsx-style arrays and objects in `class` attributes, instead of the `class:` directive

View File

@@ -0,0 +1,53 @@
> [!NOTE] `$inspect` only works during development. In a production build it becomes a noop.
The `$inspect` rune is roughly equivalent to `console.log`, with the exception that it will re-run whenever its argument changes. `$inspect` tracks reactive state deeply, meaning that updating something inside an object or array using fine-grained reactivity will cause it to re-fire (demo:
```svelte
<script>
let count = $state(0);
let message = $state('hello');
$inspect(count, message); // will console.log when `count` or `message` change
</script>
<button onclick={() => count++}>Increment</button>
<input bind:value={message} />
```
On updates, a stack trace will be printed, making it easy to find the origin of a state change (unless you're in the playground, due to technical limitations).
## $inspect(...).with
`$inspect` returns a property `with`, which you can invoke with a callback, which will then be invoked instead of `console.log`. The first argument to the callback is either `"init"` or `"update"`; subsequent arguments are the values passed to `$inspect` (demo:
```svelte
<script>
let count = $state(0);
$inspect(count).with((type, count) => {
if (type === 'update') {
debugger; // or `console.trace`, or whatever you want
}
});
</script>
<button onclick={() => count++}>Increment</button>
```
## $inspect.trace(...)
This rune, added in 5.14, causes the surrounding function to be _traced_ in development. Any time the function re-runs as part of an [effect]($effect) or a [derived]($derived), information will be printed to the console about which pieces of reactive state caused the effect to fire.
```svelte
<script>
import { doSomeWork } from './elsewhere';
$effect(() => {
+++// $inspect.trace must be the first statement of a function body+++
+++$inspect.trace();+++
doSomeWork();
});
</script>
```
`$inspect.trace` takes an optional first argument which will be used as the label.

View File

@@ -0,0 +1,166 @@
Attachments are functions that run in an [effect]($effect) when an element is mounted to the DOM or when [state]($state) read inside the function updates.
Optionally, they can return a function that is called before the attachment re-runs, or after the element is later removed from the DOM.
> [!NOTE]
> Attachments are available in Svelte 5.29 and newer.
```svelte
<!--- file: App.svelte --->
<script>
/** @type {import('svelte/attachments').Attachment} */
function myAttachment(element) {
console.log(element.nodeName); // 'DIV'
return () => {
console.log('cleaning up');
};
}
</script>
<div {@attach myAttachment}>...</div>
```
An element can have any number of attachments.
## Attachment factories
A useful pattern is for a function, such as `tooltip` in this example, to _return_ an attachment (demo:
```svelte
<!--- file: App.svelte --->
<script>
import tippy from 'tippy.js';
let content = $state('Hello!');
/**
* @param {string} content
* @returns {import('svelte/attachments').Attachment}
*/
function tooltip(content) {
return (element) => {
const tooltip = tippy(element, { content });
return tooltip.destroy;
};
}
</script>
<input bind:value={content} />
<button {@attach tooltip(content)}> Hover me </button>
```
Since the `tooltip(content)` expression runs inside an [effect]($effect), the attachment will be destroyed and recreated whenever `content` changes. The same thing would happen for any state read _inside_ the attachment function when it first runs. (If this isn't what you want, see [Controlling when attachments re-run](#Controlling-when-attachments-re-run).)
## Inline attachments
Attachments can also be created inline (demo:
```svelte
<!--- file: App.svelte --->
<canvas
width={32}
height={32}
{@attach (canvas) => {
const context = canvas.getContext('2d');
$effect(() => {
context.fillStyle = color;
context.fillRect(0, 0, canvas.width, canvas.height);
});
}}
></canvas>
```
> [!NOTE]
> The nested effect runs whenever `color` changes, while the outer effect (where `canvas.getContext(...)` is called) only runs once, since it doesn't read any reactive state.
## Conditional attachments
Falsy values like `false` or `undefined` are treated as no attachment, enabling conditional usage:
```svelte
<div {@attach enabled && myAttachment}>...</div>
```
## Passing attachments to components
When used on a component, `{@attach ...}` will create a prop whose key is a [`Symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). If the component then [spreads](/tutorial/svelte/spread-props) props onto an element, the element will receive those attachments.
This allows you to create _wrapper components_ that augment elements (demo:
```svelte
<!--- file: Button.svelte --->
<script>
/** @type {import('svelte/elements').HTMLButtonAttributes} */
let { children, ...props } = $props();
</script>
<!-- `props` includes attachments -->
<button {...props}>
{@render children?.()}
</button>
```
```svelte
<!--- file: App.svelte --->
<script>
import tippy from 'tippy.js';
import Button from './Button.svelte';
let content = $state('Hello!');
/**
* @param {string} content
* @returns {import('svelte/attachments').Attachment}
*/
function tooltip(content) {
return (element) => {
const tooltip = tippy(element, { content });
return tooltip.destroy;
};
}
</script>
<input bind:value={content} />
<Button {@attach tooltip(content)}>Hover me</Button>
```
## Controlling when attachments re-run
Attachments, unlike [actions](use), are fully reactive: `{@attach foo(bar)}` will re-run on changes to `foo` _or_ `bar` (or any state read inside `foo`):
```js
// @errors: 7006 2304 2552
function foo(bar) {
return (node) => {
veryExpensiveSetupWork(node);
update(node, bar);
};
}
```
In the rare case that this is a problem (for example, if `foo` does expensive and unavoidable setup work) consider passing the data inside a function and reading it in a child effect:
```js
// @errors: 7006 2304 2552
function foo(+++getBar+++) {
return (node) => {
veryExpensiveSetupWork(node);
+++ $effect(() => {
update(node, getBar());
});+++
}
}
```
## Creating attachments programmatically
To add attachments to an object that will be spread onto a component or element, use [`createAttachmentKey`](svelte-attachments#createAttachmentKey).
## Converting actions to attachments
If you're using a library that only provides actions, you can convert them to attachments with [`fromAction`](svelte-attachments#fromAction), allowing you to (for example) use them with components.

View File

@@ -0,0 +1,35 @@
To render a [snippet](snippet), use a `{@render ...}` tag.
```svelte
{#snippet sum(a, b)}
<p>{a} + {b} = {a + b}</p>
{/snippet}
{@render sum(1, 2)}
{@render sum(3, 4)}
{@render sum(5, 6)}
```
The expression can be an identifier like `sum`, or an arbitrary JavaScript expression:
```svelte
{@render (cool ? coolSnippet : lameSnippet)()}
```
## Optional snippets
If the snippet is potentially undefined — for example, because it's an incoming prop — then you can use optional chaining to only render it when it _is_ defined:
```svelte
{@render children?.()}
```
Alternatively, use an [`{#if ...}`](if) block with an `:else` clause to render fallback content:
```svelte
{#if children}
{@render children()}
{:else}
<p>fallback content</p>
{/if}
```

View File

@@ -0,0 +1,180 @@
As of Svelte 5.36, you can use the `await` keyword inside your components in three places where it was previously unavailable:
- at the top level of your component's `<script>`
- inside `$derived(...)` declarations
- inside your markup
This feature is currently experimental, and you must opt in by adding the `experimental.async` option wherever you [configure](/docs/kit/configuration) Svelte, usually `svelte.config.js`:
```js
/// file: svelte.config.js
export default {
compilerOptions: {
experimental: {
async: true,
},
},
};
```
The experimental flag will be removed in Svelte 6.
## Synchronized updates
When an `await` expression depends on a particular piece of state, changes to that state will not be reflected in the UI until the asynchronous work has completed, so that the UI is not left in an inconsistent state. In other words, in an example like this...
```svelte
<script>
let a = $state(1);
let b = $state(2);
async function add(a, b) {
await new Promise((f) => setTimeout(f, 500)); // artificial delay
return a + b;
}
</script>
<input type="number" bind:value={a} />
<input type="number" bind:value={b} />
<p>{a} + {b} = {await add(a, b)}</p>
```
...if you increment `a`, the contents of the `<p>` will _not_ immediately update to read this —
```html
<p>2 + 2 = 3</p>
```
— instead, the text will update to `2 + 2 = 4` when `add(a, b)` resolves.
Updates can overlap — a fast update will be reflected in the UI while an earlier slow update is still ongoing.
## Concurrency
Svelte will do as much asynchronous work as it can in parallel. For example if you have two `await` expressions in your markup...
```svelte
<p>{await one()}</p><p>{await two()}</p>
```
...both functions will run at the same time, as they are independent expressions, even though they are _visually_ sequential.
This does not apply to sequential `await` expressions inside your `<script>` or inside async functions — these run like any other asynchronous JavaScript. An exception is that independent `$derived` expressions will update independently, even though they will run sequentially when they are first created:
```js
// these will run sequentially the first time,
// but will update independently
let a = $derived(await one());
let b = $derived(await two());
```
> [!NOTE] If you write code like this, expect Svelte to give you an [`await_waterfall`](runtime-warnings#Client-warnings-await_waterfall) warning
## Indicating loading states
To render placeholder UI, you can wrap content in a `<svelte:boundary>` with a [`pending`](svelte-boundary#Properties-pending) snippet. This will be shown when the boundary is first created, but not for subsequent updates, which are globally coordinated.
After the contents of a boundary have resolved for the first time and have replaced the `pending` snippet, you can detect subsequent async work with [`$effect.pending()`]($effect#$effect.pending). This is what you would use to display a "we're asynchronously validating your input" spinner next to a form field, for example.
You can also use [`settled()`](svelte#settled) to get a promise that resolves when the current update is complete:
```js
import { tick, settled } from 'svelte';
async function onclick() {
updating = true;
// without this, the change to `updating` will be
// grouped with the other changes, meaning it
// won't be reflected in the UI
await tick();
color = 'octarine';
answer = 42;
await settled();
// any updates affected by `color` or `answer`
// have now been applied
updating = false;
}
```
## Error handling
Errors in `await` expressions will bubble to the nearest [error boundary](svelte-boundary).
## Server-side rendering
Svelte supports asynchronous server-side rendering (SSR) with the `render(...)` API. To use it, simply await the return value:
```js
/// file: server.js
import { render } from 'svelte/server';
import App from './App.svelte';
const { head, body } = +++await+++ render(App);
```
> [!NOTE] If you're using a framework like SvelteKit, this is done on your behalf.
If a `<svelte:boundary>` with a `pending` snippet is encountered during SSR, that snippet will be rendered while the rest of the content is ignored. All `await` expressions encountered outside boundaries with `pending` snippets will resolve and render their contents prior to `await render(...)` returning.
> [!NOTE] In the future, we plan to add a streaming implementation that renders the content in the background.
## Forking
The [`fork(...)`](svelte#fork) API, added in 5.42, makes it possible to run `await` expressions that you _expect_ to happen in the near future. This is mainly intended for frameworks like SvelteKit to implement preloading when (for example) users signal an intent to navigate.
```svelte
<script>
import { fork } from 'svelte';
import Menu from './Menu.svelte';
let open = $state(false);
/** @type {import('svelte').Fork | null} */
let pending = null;
function preload() {
pending ??= fork(() => {
open = true;
});
}
function discard() {
pending?.discard();
pending = null;
}
</script>
<button
onfocusin={preload}
onfocusout={discard}
onpointerenter={preload}
onpointerleave={discard}
onclick={() => {
pending?.commit();
pending = null;
// in case `pending` didn't exist
// (if it did, this is a no-op)
open = true;
}}>open menu</button
>
{#if open}
<!-- any async work inside this component will start
as soon as the fork is created -->
<Menu onclose={() => (open = false)} />
{/if}
```
## Caveats
As an experimental feature, the details of how `await` is handled (and related APIs like `$effect.pending()`) are subject to breaking changes outside of a semver major release, though we intend to keep such changes to a bare minimum.
## Breaking changes
Effects run in a slightly different order when the `experimental.async` option is `true`. Specifically, _block_ effects like `{#if ...}` and `{#each ...}` now run before an `$effect.pre` or `beforeUpdate` in the same component, which means that in very rare situations.

View File

@@ -0,0 +1,16 @@
## Function bindings
You can also use `bind:property={get, set}`, where `get` and `set` are functions, allowing you to perform validation and transformation:
```svelte
<input bind:value={() => value, (v) => (value = v.toLowerCase())} />
```
In the case of readonly bindings like [dimension bindings](#Dimensions), the `get` value should be `null`:
```svelte
<div bind:clientWidth={null, redraw} bind:clientHeight={null, redraw}>...</div>
```
> [!NOTE]
> Function bindings are available in Svelte 5.9.0 and newer.

View File

@@ -0,0 +1,42 @@
## Keyed each blocks
```svelte
<!--- copy: false --->
{#each expression as name (key)}...{/each}
```
```svelte
<!--- copy: false --->
{#each expression as name, index (key)}...{/each}
```
If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to intelligently update the list when data changes by inserting, moving and deleting items, rather than adding or removing items at the end and updating the state in the middle.
The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change.
```svelte
{#each items as item (item.id)}
<li>{item.name} x {item.qty}</li>
{/each}
<!-- or with additional index value -->
{#each items as item, i (item.id)}
<li>{i + 1}: {item.name} x {item.qty}</li>
{/each}
```
You can freely use destructuring and rest patterns in each blocks.
```svelte
{#each items as { id, name, qty }, i (id)}
<li>{i + 1}: {name} x {qty}</li>
{/each}
{#each objects as { id, ...rest }}
<li><span>{id}</span><MyComponent {...rest} /></li>
{/each}
{#each items as [id, ...rest]}
<li><span>{id}</span><MyComponent values={rest} /></li>
{/each}
```

View File

@@ -0,0 +1,100 @@
In Svelte, when you want to render asynchronous content data on the server, you can simply `await` it. This is great! However, it comes with a pitfall: when hydrating that content on the client, Svelte has to redo the asynchronous work, which blocks hydration for however long it takes:
```svelte
<script>
import { getUser } from 'my-database-library';
// This will get the user on the server, render the user's name into the h1,
// and then, during hydration on the client, it will get the user _again_,
// blocking hydration until it's done.
const user = await getUser();
</script>
<h1>{user.name}</h1>
```
That's silly, though. If we've already done the hard work of getting the data on the server, we don't want to get it again during hydration on the client. `hydratable` is a low-level API built to solve this problem. You probably won't need this very often — it will be used behind the scenes by whatever datafetching library you use. For example, it powers [remote functions in SvelteKit](/docs/kit/remote-functions).
To fix the example above:
```svelte
<script>
import { hydratable } from 'svelte';
import { getUser } from 'my-database-library';
// During server rendering, this will serialize and stash the result of `getUser`, associating
// it with the provided key and baking it into the `head` content. During hydration, it will
// look for the serialized version, returning it instead of running `getUser`. After hydration
// is done, if it's called again, it'll simply invoke `getUser`.
const user = await hydratable('user', () => getUser());
</script>
<h1>{user.name}</h1>
```
This API can also be used to provide access to random or time-based values that are stable between server rendering and hydration. For example, to get a random number that doesn't update on hydration:
```ts
import { hydratable } from 'svelte';
const rand = hydratable('random', () => Math.random());
```
If you're a library author, be sure to prefix the keys of your `hydratable` values with the name of your library so that your keys don't conflict with other libraries.
## Serialization
All data returned from a `hydratable` function must be serializable. But this doesn't mean you're limited to JSON — Svelte uses [`devalue`](https://npmjs.com/package/devalue), which can serialize all sorts of things including `Map`, `Set`, `URL`, and `BigInt`. Check the documentation page for a full list. In addition to these, thanks to some Svelte magic, you can also fearlessly use promises:
```svelte
<script>
import { hydratable } from 'svelte';
const promises = hydratable('random', () => {
return {
one: Promise.resolve(1),
two: Promise.resolve(2),
};
});
</script>
{await promises.one}
{await promises.two}
```
## CSP
`hydratable` adds an inline `<script>` block to the `head` returned from `render`. If you're using [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) (CSP), this script will likely fail to run. You can provide a `nonce` to `render`:
```js
const nonce = crypto.randomUUID();
const { head, body } = await render(App, {
csp: { nonce },
});
```
This will add the `nonce` to the script block, on the assumption that you will later add the same nonce to the CSP header of the document that contains it:
```js
response.headers.set('Content-Security-Policy', `script-src 'nonce-${nonce}'`);
```
It's essential that a `nonce` — which, British slang definition aside, means 'number used once' — is only used when dynamically server rendering an individual response.
If instead you are generating static HTML ahead of time, you must use hashes instead:
```js
const { head, body, hashes } = await render(App, {
csp: { hash: true },
});
```
`hashes.script` will be an array of strings like `["sha256-abcd123"]`. As with `nonce`, the hashes should be used in your CSP header:
```js
response.headers.set(
'Content-Security-Policy',
`script-src ${hashes.script.map((hash) => `'${hash}'`).join(' ')}`,
);
```
We recommend using `nonce` over hash if you can, as `hash` will interfere with streaming SSR in the future.

View File

@@ -0,0 +1,276 @@
```svelte
<!--- copy: false --->
{#snippet name()}...{/snippet}
```
```svelte
<!--- copy: false --->
{#snippet name(param1, param2, paramN)}...{/snippet}
```
Snippets, and [render tags](@render), are a way to create reusable chunks of markup inside your components. Instead of writing duplicative code like this...
```svelte
{#each images as image}
{#if image.href}
<a href={image.href}>
<figure>
<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
<figcaption>{image.caption}</figcaption>
</figure>
</a>
{:else}
<figure>
<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
<figcaption>{image.caption}</figcaption>
</figure>
{/if}
{/each}
```
...you can write this:
```svelte
{#snippet figure(image)}
<figure>
<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
<figcaption>{image.caption}</figcaption>
</figure>
{/snippet}
{#each images as image}
{#if image.href}
<a href={image.href}>
{@render figure(image)}
</a>
{:else}
{@render figure(image)}
{/if}
{/each}
```
Like function declarations, snippets can have an arbitrary number of parameters, which can have default values, and you can destructure each parameter. You cannot use rest parameters, however.
## Snippet scope
Snippets can be declared anywhere inside your component. They can reference values declared outside themselves, for example in the `<script>` tag or in `{#each ...}` blocks (demo...
```svelte
<script>
let { message = `it's great to see you!` } = $props();
</script>
{#snippet hello(name)}
<p>hello {name}! {message}!</p>
{/snippet}
{@render hello('alice')}
{@render hello('bob')}
```
...and they are 'visible' to everything in the same lexical scope (i.e. siblings, and children of those siblings):
```svelte
<div>
{#snippet x()}
{#snippet y()}...{/snippet}
<!-- this is fine -->
{@render y()}
{/snippet}
<!-- this will error, as `y` is not in scope -->
{@render y()}
</div>
<!-- this will also error, as `x` is not in scope -->
{@render x()}
```
Snippets can reference themselves and each other (demo:
```svelte
{#snippet blastoff()}
<span>🚀</span>
{/snippet}
{#snippet countdown(n)}
{#if n > 0}
<span>{n}...</span>
{@render countdown(n - 1)}
{:else}
{@render blastoff()}
{/if}
{/snippet}
{@render countdown(10)}
```
## Passing snippets to components
### Explicit props
Within the template, snippets are values just like any other. As such, they can be passed to components as props (demo:
```svelte
<script>
import Table from './Table.svelte';
const fruits = [
{ name: 'apples', qty: 5, price: 2 },
{ name: 'bananas', qty: 10, price: 1 },
{ name: 'cherries', qty: 20, price: 0.5 },
];
</script>
{#snippet header()}
<th>fruit</th>
<th>qty</th>
<th>price</th>
<th>total</th>
{/snippet}
{#snippet row(d)}
<td>{d.name}</td>
<td>{d.qty}</td>
<td>{d.price}</td>
<td>{d.qty * d.price}</td>
{/snippet}
<Table data={fruits} {header} {row} />
```
Think about it like passing content instead of data to a component. The concept is similar to slots in web components.
### Implicit props
As an authoring convenience, snippets declared directly _inside_ a component implicitly become props _on_ the component (demo:
```svelte
<!-- this is semantically the same as the above -->
<Table data={fruits}>
{#snippet header()}
<th>fruit</th>
<th>qty</th>
<th>price</th>
<th>total</th>
{/snippet}
{#snippet row(d)}
<td>{d.name}</td>
<td>{d.qty}</td>
<td>{d.price}</td>
<td>{d.qty * d.price}</td>
{/snippet}
</Table>
```
### Implicit `children` snippet
Any content inside the component tags that is _not_ a snippet declaration implicitly becomes part of the `children` snippet (demo:
```svelte
<!--- file: App.svelte --->
<Button>click me</Button>
```
```svelte
<!--- file: Button.svelte --->
<script>
let { children } = $props();
</script>
<!-- result will be <button>click me</button> -->
<button>{@render children()}</button>
```
> [!NOTE] Note that you cannot have a prop called `children` if you also have content inside the component — for this reason, you should avoid having props with that name
### Optional snippet props
You can declare snippet props as being optional. You can either use optional chaining to not render anything if the snippet isn't set...
```svelte
<script>
let { children } = $props();
</script>
{@render children?.()}
```
...or use an `#if` block to render fallback content:
```svelte
<script>
let { children } = $props();
</script>
{#if children}
{@render children()}
{:else}
fallback content
{/if}
```
## Typing snippets
Snippets implement the `Snippet` interface imported from `'svelte'`:
```svelte
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
data: any[];
children: Snippet;
row: Snippet<[any]>;
}
let { data, children, row }: Props = $props();
</script>
```
With this change, red squigglies will appear if you try and use the component without providing a `data` prop and a `row` snippet. Notice that the type argument provided to `Snippet` is a tuple, since snippets can have multiple parameters.
We can tighten things up further by declaring a generic, so that `data` and `row` refer to the same type:
```svelte
<script lang="ts" generics="T">
import type { Snippet } from 'svelte';
let {
data,
children,
row,
}: {
data: T[];
children: Snippet;
row: Snippet<[T]>;
} = $props();
</script>
```
## Exporting snippets
Snippets declared at the top level of a `.svelte` file can be exported from a `<script module>` for use in other components, provided they don't reference any declarations in a non-module `<script>` (whether directly or indirectly, via other snippets) (demo:
```svelte
<script module>
export { add };
</script>
{#snippet add(a, b)}
{a} + {b} = {a + b}
{/snippet}
```
> [!NOTE]
> This requires Svelte 5.5.0 or newer
## Programmatic snippets
Snippets can be created programmatically with the [`createRawSnippet`](svelte#createRawSnippet) API. This is intended for advanced use cases.
## Snippets and slots
In Svelte 4, content can be passed to components using [slots](legacy-slots). Snippets are more powerful and flexible, and so slots have been deprecated in Svelte 5.

View File

@@ -0,0 +1,61 @@
## createSubscriber
<blockquote class="since note">
Available since 5.7.0
</blockquote>
Returns a `subscribe` function that integrates external event-based systems with Svelte's reactivity.
It's particularly useful for integrating with web APIs like `MediaQuery`, `IntersectionObserver`, or `WebSocket`.
If `subscribe` is called inside an effect (including indirectly, for example inside a getter),
the `start` callback will be called with an `update` function. Whenever `update` is called, the effect re-runs.
If `start` returns a cleanup function, it will be called when the effect is destroyed.
If `subscribe` is called in multiple effects, `start` will only be called once as long as the effects
are active, and the returned teardown function will only be called when all effects are destroyed.
It's best understood with an example. Here's an implementation of [`MediaQuery`](/docs/svelte/svelte-reactivity#MediaQuery):
```js
// @errors: 7031
import { createSubscriber } from 'svelte/reactivity';
import { on } from 'svelte/events';
export class MediaQuery {
#query;
#subscribe;
constructor(query) {
this.#query = window.matchMedia(`(${query})`);
this.#subscribe = createSubscriber((update) => {
// when the `change` event occurs, re-run any effects that read `this.current`
const off = on(this.#query, 'change', update);
// stop listening when all the effects are destroyed
return () => off();
});
}
get current() {
// This makes the getter reactive, if read in an effect
this.#subscribe();
// Return the current state of the query, whether or not we're in an effect
return this.#query.matches;
}
}
```
<div class="ts-block">
```dts
function createSubscriber(
start: (update: () => void) => (() => void) | void
): () => void;
```
</div>

View File

@@ -0,0 +1,9 @@
{
"name": "svelte",
"description": "A plugin for all things related to Svelte development, MCP, skills, and more.",
"version": "1.0.4",
"author": {
"name": "Svelte"
},
"keywords": ["svelte", "sveltekit", "mcp", "autofixer"]
}

View File

@@ -0,0 +1,8 @@
{
"mcpServers": {
"svelte": {
"type": "http",
"url": "https://mcp.svelte.dev/mcp"
}
}
}

View File

@@ -0,0 +1,67 @@
---
name: svelte-file-editor
description: Specialized Svelte 5 code editor. MUST BE USED PROACTIVELY when creating, editing, or reviewing any .svelte file or .svelte.ts/.svelte.js module and MUST use the tools from the MCP server or the `svelte-file-editor` skill if they are available. Fetches relevant documentation and validates code using the Svelte MCP server tools.
---
You are a Svelte 5 expert responsible for writing, editing, and validating Svelte components and modules. You have access to the Svelte MCP server which provides documentation and code analysis tools. Always use the tools from the svelte MCP server to fetch documentation with `get_documentation` and validating the code with `svelte_autofixer`. If the autofixer returns any issue or suggestions try to solve them.
If the MCP tools are not available you can use the `svelte-code-writer` skill to learn how to use the `@sveltejs/mcp` cli to access the same tools.
If the skill is not available you can run `npx @sveltejs/mcp@latest -y --help` to learn how to use it.
## Available MCP Tools
### 1. list-sections
Lists all available Svelte 5 and SvelteKit documentation sections with titles and paths. Use this first to discover what documentation is available.
### 2. get-documentation
Retrieves full documentation for specified sections. Accepts a single section name or an array of section names. Use after `list-sections` to fetch relevant docs for the task at hand.
**Example sections:** `$state`, `$derived`, `$effect`, `$props`, `$bindable`, `snippets`, `routing`, `load functions`
### 3. svelte-autofixer
Analyzes Svelte code and returns suggestions to fix issues. Pass the component code directly to this tool. It will detect common mistakes like:
- Using `$effect` instead of `$derived` for computations
- Missing cleanup in effects
- Svelte 4 syntax (`on:click`, `export let`, `<slot>`)
- Missing keys in `{#each}` blocks
- And more
## Workflow
When invoked to work on a Svelte file:
### 1. Gather Context (if needed)
If you're uncertain about Svelte 5 syntax or patterns, use the MCP tools:
1. Call `list-sections` to see available documentation
2. Call `get-documentation` with relevant section names
### 2. Read the Target File
Read the file to understand the current implementation.
### 3. Make Changes
Apply edits following Svelte 5 best practices:
### 4. Validate Changes
After editing, ALWAYS call `svelte-autofixer` with the updated code to check for issues.
### 5. Fix Any Issues
If the autofixer reports problems, fix them and re-validate until no issues remain.
## Output Format
After completing your work, provide:
1. Summary of changes made
2. Any issues found and fixed by the autofixer
3. Recommendations for further improvements (if any)

View File

@@ -0,0 +1,28 @@
---
description: Instructions for using the Svelte MCP server tools for documentation lookup, code analysis, and validation
alwaysApply: true
---
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available Svelte MCP Tools:
### 1. list-sections
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
### 2. get-documentation
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
### 3. svelte-autofixer
Analyzes Svelte code and returns issues and suggestions.
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
### 4. playground-link
Generates a Svelte Playground link with the provided code.
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.

View File

@@ -0,0 +1,66 @@
---
name: svelte-code-writer
description: CLI tools for Svelte 5 documentation lookup and code analysis. MUST be used whenever creating, editing or analyzing any Svelte component (.svelte) or Svelte module (.svelte.ts/.svelte.js). If possible, this skill should be executed within the svelte-file-editor agent for optimal results.
---
# Svelte 5 Code Writer
## CLI Tools
You have access to `@sveltejs/mcp` CLI for Svelte-specific assistance. Use these commands via `npx`:
### List Documentation Sections
```bash
npx @sveltejs/mcp list-sections
```
Lists all available Svelte 5 and SvelteKit documentation sections with titles and paths.
### Get Documentation
```bash
npx @sveltejs/mcp get-documentation "<section1>,<section2>,..."
```
Retrieves full documentation for specified sections. Use after `list-sections` to fetch relevant docs.
**Example:**
```bash
npx @sveltejs/mcp get-documentation "$state,$derived,$effect"
```
### Svelte Autofixer
```bash
npx @sveltejs/mcp svelte-autofixer "<code_or_path>" [options]
```
Analyzes Svelte code and suggests fixes for common issues.
**Options:**
- `--async` - Enable async Svelte mode (default: false)
- `--svelte-version` - Target version: 4 or 5 (default: 5)
**Examples:**
```bash
# Analyze inline code (escape $ as \$)
npx @sveltejs/mcp svelte-autofixer '<script>let count = \$state(0);</script>'
# Analyze a file
npx @sveltejs/mcp svelte-autofixer ./src/lib/Component.svelte
# Target Svelte 4
npx @sveltejs/mcp svelte-autofixer ./Component.svelte --svelte-version 4
```
**Important:** When passing code with runes (`$state`, `$derived`, etc.) via the terminal, escape the `$` character as `\$` to prevent shell variable substitution.
## Workflow
1. **Uncertain about syntax?** Run `list-sections` then `get-documentation` for relevant topics
2. **Reviewing/debugging?** Run `svelte-autofixer` on the code to detect issues
3. **Always validate** - Run `svelte-autofixer` before finalizing any Svelte component

View File

@@ -0,0 +1,176 @@
---
name: svelte-core-bestpractices
description: Guidance on writing fast, robust, modern Svelte code. Load this skill whenever in a Svelte project and asked to write/edit or analyze a Svelte component or module. Covers reactivity, event handling, styling, integration with libraries and more.
---
## `$state`
Only use the `$state` rune for variables that should be _reactive_ — in other words, variables that cause an `$effect`, `$derived` or template expression to update. Everything else can be a normal variable.
Objects and arrays (`$state({...})` or `$state([...])`) are made deeply reactive, meaning mutation will trigger updates. This has a trade-off: in exchange for fine-grained reactivity, the objects must be proxied, which has performance overhead. In cases where you're dealing with large objects that are only ever reassigned (rather than mutated), use `$state.raw` instead. This is often the case with API responses, for example.
## `$derived`
To compute something from state, use `$derived` rather than `$effect`:
```js
// do this
let square = $derived(num * num);
// don't do this
let square;
$effect(() => {
square = num * num;
});
```
> [!NOTE] `$derived` is given an expression, _not_ a function. If you need to use a function (because the expression is complex, for example) use `$derived.by`.
Deriveds are writable — you can assign to them, just like `$state`, except that they will re-evaluate when their expression changes.
If the derived expression is an object or array, it will be returned as-is — it is _not_ made deeply reactive. You can, however, use `$state` inside `$derived.by` in the rare cases that you need this.
## `$effect`
Effects are an escape hatch and should mostly be avoided. In particular, avoid updating state inside effects.
- If you need to sync state to an external library such as D3, it is often neater to use [`{@attach ...}`](references/@attach.md)
- If you need to run some code in response to user interaction, put the code directly in an event handler or use a [function binding](references/bind.md) as appropriate
- If you need to log values for debugging purposes, use [`$inspect`](references/$inspect.md)
- If you need to observe something external to Svelte, use [`createSubscriber`](references/svelte-reactivity.md)
Never wrap the contents of an effect in `if (browser) {...}` or similar — effects do not run on the server.
## `$props`
Treat props as though they will change. For example, values that depend on props should usually use `$derived`:
```js
// @errors: 2451
let { type } = $props();
// do this
let color = $derived(type === 'danger' ? 'red' : 'green');
// don't do this — `color` will not update if `type` changes
let color = type === 'danger' ? 'red' : 'green';
```
## `$inspect.trace`
`$inspect.trace` is a debugging tool for reactivity. If something is not updating properly or running more than it should you can add `$inspect.trace(label)` as the first line of an `$effect` or `$derived.by` (or any function they call) to trace their dependencies and discover which one triggered an update.
## Events
Any element attribute starting with `on` is treated as an event listener:
```svelte
<button onclick={() => {...}}>click me</button>
<!-- attribute shorthand also works -->
<button {onclick}>...</button>
<!-- so do spread attributes -->
<button {...props}>...</button>
```
If you need to attach listeners to `window` or `document` you can use `<svelte:window>` and `<svelte:document>`:
```svelte
<svelte:window onkeydown={...} />
<svelte:document onvisibilitychange={...} />
```
Avoid using `onMount` or `$effect` for this.
## Snippets
[Snippets](references/snippet.md) are a way to define reusable chunks of markup that can be instantiated with the [`{@render ...}`](references/@render.md) tag, or passed to components as props. They must be declared within the template.
```svelte
{#snippet greeting(name)}
<p>hello {name}!</p>
{/snippet}
{@render greeting('world')}
```
> [!NOTE] Snippets declared at the top level of a component (i.e. not inside elements or blocks) can be referenced inside `<script>`. A snippet that doesn't reference component state is also available in a `<script module>`, in which case it can be exported for use by other components.
## Each blocks
Prefer to use [keyed each blocks](references/each.md) — this improves performance by allowing Svelte to surgically insert or remove items rather than updating the DOM belonging to existing items.
> [!NOTE] The key _must_ uniquely identify the object. Do not use the index as a key.
Avoid destructuring if you need to mutate the item (with something like `bind:value={item.count}`, for example).
## Using JavaScript variables in CSS
If you have a JS variable that you want to use inside CSS you can set a CSS custom property with the `style:` directive.
```svelte
<div style:--columns={columns}>...</div>
```
You can then reference `var(--columns)` inside the component's `<style>`.
## Styling child components
The CSS in a component's `<style>` is scoped to that component. If a parent component needs to control the child's styles, the preferred way is to use CSS custom properties:
```svelte
<!-- Parent.svelte -->
<Child --color="red" />
<!-- Child.svelte -->
<h1>Hello</h1>
<style>
h1 {
color: var(--color);
}
</style>
```
If this is impossible (for example, the child component comes from a library) you can use `:global` to override styles:
```svelte
<div>
<Child />
</div>
<style>
div :global {
h1 {
color: red;
}
}
</style>
```
## Context
Consider using context instead of declaring state in a shared module. This will scope the state to the part of the app that needs it, and eliminate the possibility of it leaking between users when server-side rendering.
Use `createContext` rather than `setContext` and `getContext`, as it provides type safety.
## Async Svelte
If using version 5.36 or higher, you can use [await expressions](references/await-expressions.md) and [hydratable](references/hydratable.md) to use promises directly inside components. Note that these require the `experimental.async` option to be enabled in `svelte.config.js` as they are not yet considered fully stable.
## Avoid legacy features
Always use runes mode for new code, and avoid features that have more modern replacements:
- use `$state` instead of implicit reactivity (e.g. `let count = 0; count += 1`)
- use `$derived` and `$effect` instead of `$:` assignments and statements (but only use effects when there is no better solution)
- use `$props` instead of `export let`, `$$props` and `$$restProps`
- use `onclick={...}` instead of `on:click={...}`
- use `{#snippet ...}` and `{@render ...}` instead of `<slot>` and `$$slots` and `<svelte:fragment>`
- use `<DynamicComponent>` instead of `<svelte:component this={DynamicComponent}>`
- use `import Self from './ThisComponent.svelte'` and `<Self>` instead of `<svelte:self>`
- use classes with `$state` fields to share reactivity between components, instead of using stores
- use `{@attach ...}` instead of `use:action`
- use clsx-style arrays and objects in `class` attributes, instead of the `class:` directive

View File

@@ -0,0 +1,53 @@
> [!NOTE] `$inspect` only works during development. In a production build it becomes a noop.
The `$inspect` rune is roughly equivalent to `console.log`, with the exception that it will re-run whenever its argument changes. `$inspect` tracks reactive state deeply, meaning that updating something inside an object or array using fine-grained reactivity will cause it to re-fire (demo:
```svelte
<script>
let count = $state(0);
let message = $state('hello');
$inspect(count, message); // will console.log when `count` or `message` change
</script>
<button onclick={() => count++}>Increment</button>
<input bind:value={message} />
```
On updates, a stack trace will be printed, making it easy to find the origin of a state change (unless you're in the playground, due to technical limitations).
## $inspect(...).with
`$inspect` returns a property `with`, which you can invoke with a callback, which will then be invoked instead of `console.log`. The first argument to the callback is either `"init"` or `"update"`; subsequent arguments are the values passed to `$inspect` (demo:
```svelte
<script>
let count = $state(0);
$inspect(count).with((type, count) => {
if (type === 'update') {
debugger; // or `console.trace`, or whatever you want
}
});
</script>
<button onclick={() => count++}>Increment</button>
```
## $inspect.trace(...)
This rune, added in 5.14, causes the surrounding function to be _traced_ in development. Any time the function re-runs as part of an [effect]($effect) or a [derived]($derived), information will be printed to the console about which pieces of reactive state caused the effect to fire.
```svelte
<script>
import { doSomeWork } from './elsewhere';
$effect(() => {
+++// $inspect.trace must be the first statement of a function body+++
+++$inspect.trace();+++
doSomeWork();
});
</script>
```
`$inspect.trace` takes an optional first argument which will be used as the label.

View File

@@ -0,0 +1,166 @@
Attachments are functions that run in an [effect]($effect) when an element is mounted to the DOM or when [state]($state) read inside the function updates.
Optionally, they can return a function that is called before the attachment re-runs, or after the element is later removed from the DOM.
> [!NOTE]
> Attachments are available in Svelte 5.29 and newer.
```svelte
<!--- file: App.svelte --->
<script>
/** @type {import('svelte/attachments').Attachment} */
function myAttachment(element) {
console.log(element.nodeName); // 'DIV'
return () => {
console.log('cleaning up');
};
}
</script>
<div {@attach myAttachment}>...</div>
```
An element can have any number of attachments.
## Attachment factories
A useful pattern is for a function, such as `tooltip` in this example, to _return_ an attachment (demo:
```svelte
<!--- file: App.svelte --->
<script>
import tippy from 'tippy.js';
let content = $state('Hello!');
/**
* @param {string} content
* @returns {import('svelte/attachments').Attachment}
*/
function tooltip(content) {
return (element) => {
const tooltip = tippy(element, { content });
return tooltip.destroy;
};
}
</script>
<input bind:value={content} />
<button {@attach tooltip(content)}> Hover me </button>
```
Since the `tooltip(content)` expression runs inside an [effect]($effect), the attachment will be destroyed and recreated whenever `content` changes. The same thing would happen for any state read _inside_ the attachment function when it first runs. (If this isn't what you want, see [Controlling when attachments re-run](#Controlling-when-attachments-re-run).)
## Inline attachments
Attachments can also be created inline (demo:
```svelte
<!--- file: App.svelte --->
<canvas
width={32}
height={32}
{@attach (canvas) => {
const context = canvas.getContext('2d');
$effect(() => {
context.fillStyle = color;
context.fillRect(0, 0, canvas.width, canvas.height);
});
}}
></canvas>
```
> [!NOTE]
> The nested effect runs whenever `color` changes, while the outer effect (where `canvas.getContext(...)` is called) only runs once, since it doesn't read any reactive state.
## Conditional attachments
Falsy values like `false` or `undefined` are treated as no attachment, enabling conditional usage:
```svelte
<div {@attach enabled && myAttachment}>...</div>
```
## Passing attachments to components
When used on a component, `{@attach ...}` will create a prop whose key is a [`Symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). If the component then [spreads](/tutorial/svelte/spread-props) props onto an element, the element will receive those attachments.
This allows you to create _wrapper components_ that augment elements (demo:
```svelte
<!--- file: Button.svelte --->
<script>
/** @type {import('svelte/elements').HTMLButtonAttributes} */
let { children, ...props } = $props();
</script>
<!-- `props` includes attachments -->
<button {...props}>
{@render children?.()}
</button>
```
```svelte
<!--- file: App.svelte --->
<script>
import tippy from 'tippy.js';
import Button from './Button.svelte';
let content = $state('Hello!');
/**
* @param {string} content
* @returns {import('svelte/attachments').Attachment}
*/
function tooltip(content) {
return (element) => {
const tooltip = tippy(element, { content });
return tooltip.destroy;
};
}
</script>
<input bind:value={content} />
<Button {@attach tooltip(content)}>Hover me</Button>
```
## Controlling when attachments re-run
Attachments, unlike [actions](use), are fully reactive: `{@attach foo(bar)}` will re-run on changes to `foo` _or_ `bar` (or any state read inside `foo`):
```js
// @errors: 7006 2304 2552
function foo(bar) {
return (node) => {
veryExpensiveSetupWork(node);
update(node, bar);
};
}
```
In the rare case that this is a problem (for example, if `foo` does expensive and unavoidable setup work) consider passing the data inside a function and reading it in a child effect:
```js
// @errors: 7006 2304 2552
function foo(+++getBar+++) {
return (node) => {
veryExpensiveSetupWork(node);
+++ $effect(() => {
update(node, getBar());
});+++
}
}
```
## Creating attachments programmatically
To add attachments to an object that will be spread onto a component or element, use [`createAttachmentKey`](svelte-attachments#createAttachmentKey).
## Converting actions to attachments
If you're using a library that only provides actions, you can convert them to attachments with [`fromAction`](svelte-attachments#fromAction), allowing you to (for example) use them with components.

View File

@@ -0,0 +1,35 @@
To render a [snippet](snippet), use a `{@render ...}` tag.
```svelte
{#snippet sum(a, b)}
<p>{a} + {b} = {a + b}</p>
{/snippet}
{@render sum(1, 2)}
{@render sum(3, 4)}
{@render sum(5, 6)}
```
The expression can be an identifier like `sum`, or an arbitrary JavaScript expression:
```svelte
{@render (cool ? coolSnippet : lameSnippet)()}
```
## Optional snippets
If the snippet is potentially undefined — for example, because it's an incoming prop — then you can use optional chaining to only render it when it _is_ defined:
```svelte
{@render children?.()}
```
Alternatively, use an [`{#if ...}`](if) block with an `:else` clause to render fallback content:
```svelte
{#if children}
{@render children()}
{:else}
<p>fallback content</p>
{/if}
```

View File

@@ -0,0 +1,180 @@
As of Svelte 5.36, you can use the `await` keyword inside your components in three places where it was previously unavailable:
- at the top level of your component's `<script>`
- inside `$derived(...)` declarations
- inside your markup
This feature is currently experimental, and you must opt in by adding the `experimental.async` option wherever you [configure](/docs/kit/configuration) Svelte, usually `svelte.config.js`:
```js
/// file: svelte.config.js
export default {
compilerOptions: {
experimental: {
async: true,
},
},
};
```
The experimental flag will be removed in Svelte 6.
## Synchronized updates
When an `await` expression depends on a particular piece of state, changes to that state will not be reflected in the UI until the asynchronous work has completed, so that the UI is not left in an inconsistent state. In other words, in an example like this...
```svelte
<script>
let a = $state(1);
let b = $state(2);
async function add(a, b) {
await new Promise((f) => setTimeout(f, 500)); // artificial delay
return a + b;
}
</script>
<input type="number" bind:value={a} />
<input type="number" bind:value={b} />
<p>{a} + {b} = {await add(a, b)}</p>
```
...if you increment `a`, the contents of the `<p>` will _not_ immediately update to read this —
```html
<p>2 + 2 = 3</p>
```
— instead, the text will update to `2 + 2 = 4` when `add(a, b)` resolves.
Updates can overlap — a fast update will be reflected in the UI while an earlier slow update is still ongoing.
## Concurrency
Svelte will do as much asynchronous work as it can in parallel. For example if you have two `await` expressions in your markup...
```svelte
<p>{await one()}</p><p>{await two()}</p>
```
...both functions will run at the same time, as they are independent expressions, even though they are _visually_ sequential.
This does not apply to sequential `await` expressions inside your `<script>` or inside async functions — these run like any other asynchronous JavaScript. An exception is that independent `$derived` expressions will update independently, even though they will run sequentially when they are first created:
```js
// these will run sequentially the first time,
// but will update independently
let a = $derived(await one());
let b = $derived(await two());
```
> [!NOTE] If you write code like this, expect Svelte to give you an [`await_waterfall`](runtime-warnings#Client-warnings-await_waterfall) warning
## Indicating loading states
To render placeholder UI, you can wrap content in a `<svelte:boundary>` with a [`pending`](svelte-boundary#Properties-pending) snippet. This will be shown when the boundary is first created, but not for subsequent updates, which are globally coordinated.
After the contents of a boundary have resolved for the first time and have replaced the `pending` snippet, you can detect subsequent async work with [`$effect.pending()`]($effect#$effect.pending). This is what you would use to display a "we're asynchronously validating your input" spinner next to a form field, for example.
You can also use [`settled()`](svelte#settled) to get a promise that resolves when the current update is complete:
```js
import { tick, settled } from 'svelte';
async function onclick() {
updating = true;
// without this, the change to `updating` will be
// grouped with the other changes, meaning it
// won't be reflected in the UI
await tick();
color = 'octarine';
answer = 42;
await settled();
// any updates affected by `color` or `answer`
// have now been applied
updating = false;
}
```
## Error handling
Errors in `await` expressions will bubble to the nearest [error boundary](svelte-boundary).
## Server-side rendering
Svelte supports asynchronous server-side rendering (SSR) with the `render(...)` API. To use it, simply await the return value:
```js
/// file: server.js
import { render } from 'svelte/server';
import App from './App.svelte';
const { head, body } = +++await+++ render(App);
```
> [!NOTE] If you're using a framework like SvelteKit, this is done on your behalf.
If a `<svelte:boundary>` with a `pending` snippet is encountered during SSR, that snippet will be rendered while the rest of the content is ignored. All `await` expressions encountered outside boundaries with `pending` snippets will resolve and render their contents prior to `await render(...)` returning.
> [!NOTE] In the future, we plan to add a streaming implementation that renders the content in the background.
## Forking
The [`fork(...)`](svelte#fork) API, added in 5.42, makes it possible to run `await` expressions that you _expect_ to happen in the near future. This is mainly intended for frameworks like SvelteKit to implement preloading when (for example) users signal an intent to navigate.
```svelte
<script>
import { fork } from 'svelte';
import Menu from './Menu.svelte';
let open = $state(false);
/** @type {import('svelte').Fork | null} */
let pending = null;
function preload() {
pending ??= fork(() => {
open = true;
});
}
function discard() {
pending?.discard();
pending = null;
}
</script>
<button
onfocusin={preload}
onfocusout={discard}
onpointerenter={preload}
onpointerleave={discard}
onclick={() => {
pending?.commit();
pending = null;
// in case `pending` didn't exist
// (if it did, this is a no-op)
open = true;
}}>open menu</button
>
{#if open}
<!-- any async work inside this component will start
as soon as the fork is created -->
<Menu onclose={() => (open = false)} />
{/if}
```
## Caveats
As an experimental feature, the details of how `await` is handled (and related APIs like `$effect.pending()`) are subject to breaking changes outside of a semver major release, though we intend to keep such changes to a bare minimum.
## Breaking changes
Effects run in a slightly different order when the `experimental.async` option is `true`. Specifically, _block_ effects like `{#if ...}` and `{#each ...}` now run before an `$effect.pre` or `beforeUpdate` in the same component, which means that in very rare situations.

View File

@@ -0,0 +1,16 @@
## Function bindings
You can also use `bind:property={get, set}`, where `get` and `set` are functions, allowing you to perform validation and transformation:
```svelte
<input bind:value={() => value, (v) => (value = v.toLowerCase())} />
```
In the case of readonly bindings like [dimension bindings](#Dimensions), the `get` value should be `null`:
```svelte
<div bind:clientWidth={null, redraw} bind:clientHeight={null, redraw}>...</div>
```
> [!NOTE]
> Function bindings are available in Svelte 5.9.0 and newer.

View File

@@ -0,0 +1,42 @@
## Keyed each blocks
```svelte
<!--- copy: false --->
{#each expression as name (key)}...{/each}
```
```svelte
<!--- copy: false --->
{#each expression as name, index (key)}...{/each}
```
If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to intelligently update the list when data changes by inserting, moving and deleting items, rather than adding or removing items at the end and updating the state in the middle.
The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change.
```svelte
{#each items as item (item.id)}
<li>{item.name} x {item.qty}</li>
{/each}
<!-- or with additional index value -->
{#each items as item, i (item.id)}
<li>{i + 1}: {item.name} x {item.qty}</li>
{/each}
```
You can freely use destructuring and rest patterns in each blocks.
```svelte
{#each items as { id, name, qty }, i (id)}
<li>{i + 1}: {name} x {qty}</li>
{/each}
{#each objects as { id, ...rest }}
<li><span>{id}</span><MyComponent {...rest} /></li>
{/each}
{#each items as [id, ...rest]}
<li><span>{id}</span><MyComponent values={rest} /></li>
{/each}
```

View File

@@ -0,0 +1,100 @@
In Svelte, when you want to render asynchronous content data on the server, you can simply `await` it. This is great! However, it comes with a pitfall: when hydrating that content on the client, Svelte has to redo the asynchronous work, which blocks hydration for however long it takes:
```svelte
<script>
import { getUser } from 'my-database-library';
// This will get the user on the server, render the user's name into the h1,
// and then, during hydration on the client, it will get the user _again_,
// blocking hydration until it's done.
const user = await getUser();
</script>
<h1>{user.name}</h1>
```
That's silly, though. If we've already done the hard work of getting the data on the server, we don't want to get it again during hydration on the client. `hydratable` is a low-level API built to solve this problem. You probably won't need this very often — it will be used behind the scenes by whatever datafetching library you use. For example, it powers [remote functions in SvelteKit](/docs/kit/remote-functions).
To fix the example above:
```svelte
<script>
import { hydratable } from 'svelte';
import { getUser } from 'my-database-library';
// During server rendering, this will serialize and stash the result of `getUser`, associating
// it with the provided key and baking it into the `head` content. During hydration, it will
// look for the serialized version, returning it instead of running `getUser`. After hydration
// is done, if it's called again, it'll simply invoke `getUser`.
const user = await hydratable('user', () => getUser());
</script>
<h1>{user.name}</h1>
```
This API can also be used to provide access to random or time-based values that are stable between server rendering and hydration. For example, to get a random number that doesn't update on hydration:
```ts
import { hydratable } from 'svelte';
const rand = hydratable('random', () => Math.random());
```
If you're a library author, be sure to prefix the keys of your `hydratable` values with the name of your library so that your keys don't conflict with other libraries.
## Serialization
All data returned from a `hydratable` function must be serializable. But this doesn't mean you're limited to JSON — Svelte uses [`devalue`](https://npmjs.com/package/devalue), which can serialize all sorts of things including `Map`, `Set`, `URL`, and `BigInt`. Check the documentation page for a full list. In addition to these, thanks to some Svelte magic, you can also fearlessly use promises:
```svelte
<script>
import { hydratable } from 'svelte';
const promises = hydratable('random', () => {
return {
one: Promise.resolve(1),
two: Promise.resolve(2),
};
});
</script>
{await promises.one}
{await promises.two}
```
## CSP
`hydratable` adds an inline `<script>` block to the `head` returned from `render`. If you're using [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) (CSP), this script will likely fail to run. You can provide a `nonce` to `render`:
```js
const nonce = crypto.randomUUID();
const { head, body } = await render(App, {
csp: { nonce },
});
```
This will add the `nonce` to the script block, on the assumption that you will later add the same nonce to the CSP header of the document that contains it:
```js
response.headers.set('Content-Security-Policy', `script-src 'nonce-${nonce}'`);
```
It's essential that a `nonce` — which, British slang definition aside, means 'number used once' — is only used when dynamically server rendering an individual response.
If instead you are generating static HTML ahead of time, you must use hashes instead:
```js
const { head, body, hashes } = await render(App, {
csp: { hash: true },
});
```
`hashes.script` will be an array of strings like `["sha256-abcd123"]`. As with `nonce`, the hashes should be used in your CSP header:
```js
response.headers.set(
'Content-Security-Policy',
`script-src ${hashes.script.map((hash) => `'${hash}'`).join(' ')}`,
);
```
We recommend using `nonce` over hash if you can, as `hash` will interfere with streaming SSR in the future.

View File

@@ -0,0 +1,276 @@
```svelte
<!--- copy: false --->
{#snippet name()}...{/snippet}
```
```svelte
<!--- copy: false --->
{#snippet name(param1, param2, paramN)}...{/snippet}
```
Snippets, and [render tags](@render), are a way to create reusable chunks of markup inside your components. Instead of writing duplicative code like this...
```svelte
{#each images as image}
{#if image.href}
<a href={image.href}>
<figure>
<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
<figcaption>{image.caption}</figcaption>
</figure>
</a>
{:else}
<figure>
<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
<figcaption>{image.caption}</figcaption>
</figure>
{/if}
{/each}
```
...you can write this:
```svelte
{#snippet figure(image)}
<figure>
<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
<figcaption>{image.caption}</figcaption>
</figure>
{/snippet}
{#each images as image}
{#if image.href}
<a href={image.href}>
{@render figure(image)}
</a>
{:else}
{@render figure(image)}
{/if}
{/each}
```
Like function declarations, snippets can have an arbitrary number of parameters, which can have default values, and you can destructure each parameter. You cannot use rest parameters, however.
## Snippet scope
Snippets can be declared anywhere inside your component. They can reference values declared outside themselves, for example in the `<script>` tag or in `{#each ...}` blocks (demo...
```svelte
<script>
let { message = `it's great to see you!` } = $props();
</script>
{#snippet hello(name)}
<p>hello {name}! {message}!</p>
{/snippet}
{@render hello('alice')}
{@render hello('bob')}
```
...and they are 'visible' to everything in the same lexical scope (i.e. siblings, and children of those siblings):
```svelte
<div>
{#snippet x()}
{#snippet y()}...{/snippet}
<!-- this is fine -->
{@render y()}
{/snippet}
<!-- this will error, as `y` is not in scope -->
{@render y()}
</div>
<!-- this will also error, as `x` is not in scope -->
{@render x()}
```
Snippets can reference themselves and each other (demo:
```svelte
{#snippet blastoff()}
<span>🚀</span>
{/snippet}
{#snippet countdown(n)}
{#if n > 0}
<span>{n}...</span>
{@render countdown(n - 1)}
{:else}
{@render blastoff()}
{/if}
{/snippet}
{@render countdown(10)}
```
## Passing snippets to components
### Explicit props
Within the template, snippets are values just like any other. As such, they can be passed to components as props (demo:
```svelte
<script>
import Table from './Table.svelte';
const fruits = [
{ name: 'apples', qty: 5, price: 2 },
{ name: 'bananas', qty: 10, price: 1 },
{ name: 'cherries', qty: 20, price: 0.5 },
];
</script>
{#snippet header()}
<th>fruit</th>
<th>qty</th>
<th>price</th>
<th>total</th>
{/snippet}
{#snippet row(d)}
<td>{d.name}</td>
<td>{d.qty}</td>
<td>{d.price}</td>
<td>{d.qty * d.price}</td>
{/snippet}
<Table data={fruits} {header} {row} />
```
Think about it like passing content instead of data to a component. The concept is similar to slots in web components.
### Implicit props
As an authoring convenience, snippets declared directly _inside_ a component implicitly become props _on_ the component (demo:
```svelte
<!-- this is semantically the same as the above -->
<Table data={fruits}>
{#snippet header()}
<th>fruit</th>
<th>qty</th>
<th>price</th>
<th>total</th>
{/snippet}
{#snippet row(d)}
<td>{d.name}</td>
<td>{d.qty}</td>
<td>{d.price}</td>
<td>{d.qty * d.price}</td>
{/snippet}
</Table>
```
### Implicit `children` snippet
Any content inside the component tags that is _not_ a snippet declaration implicitly becomes part of the `children` snippet (demo:
```svelte
<!--- file: App.svelte --->
<Button>click me</Button>
```
```svelte
<!--- file: Button.svelte --->
<script>
let { children } = $props();
</script>
<!-- result will be <button>click me</button> -->
<button>{@render children()}</button>
```
> [!NOTE] Note that you cannot have a prop called `children` if you also have content inside the component — for this reason, you should avoid having props with that name
### Optional snippet props
You can declare snippet props as being optional. You can either use optional chaining to not render anything if the snippet isn't set...
```svelte
<script>
let { children } = $props();
</script>
{@render children?.()}
```
...or use an `#if` block to render fallback content:
```svelte
<script>
let { children } = $props();
</script>
{#if children}
{@render children()}
{:else}
fallback content
{/if}
```
## Typing snippets
Snippets implement the `Snippet` interface imported from `'svelte'`:
```svelte
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
data: any[];
children: Snippet;
row: Snippet<[any]>;
}
let { data, children, row }: Props = $props();
</script>
```
With this change, red squigglies will appear if you try and use the component without providing a `data` prop and a `row` snippet. Notice that the type argument provided to `Snippet` is a tuple, since snippets can have multiple parameters.
We can tighten things up further by declaring a generic, so that `data` and `row` refer to the same type:
```svelte
<script lang="ts" generics="T">
import type { Snippet } from 'svelte';
let {
data,
children,
row,
}: {
data: T[];
children: Snippet;
row: Snippet<[T]>;
} = $props();
</script>
```
## Exporting snippets
Snippets declared at the top level of a `.svelte` file can be exported from a `<script module>` for use in other components, provided they don't reference any declarations in a non-module `<script>` (whether directly or indirectly, via other snippets) (demo:
```svelte
<script module>
export { add };
</script>
{#snippet add(a, b)}
{a} + {b} = {a + b}
{/snippet}
```
> [!NOTE]
> This requires Svelte 5.5.0 or newer
## Programmatic snippets
Snippets can be created programmatically with the [`createRawSnippet`](svelte#createRawSnippet) API. This is intended for advanced use cases.
## Snippets and slots
In Svelte 4, content can be passed to components using [slots](legacy-slots). Snippets are more powerful and flexible, and so slots have been deprecated in Svelte 5.

View File

@@ -0,0 +1,61 @@
## createSubscriber
<blockquote class="since note">
Available since 5.7.0
</blockquote>
Returns a `subscribe` function that integrates external event-based systems with Svelte's reactivity.
It's particularly useful for integrating with web APIs like `MediaQuery`, `IntersectionObserver`, or `WebSocket`.
If `subscribe` is called inside an effect (including indirectly, for example inside a getter),
the `start` callback will be called with an `update` function. Whenever `update` is called, the effect re-runs.
If `start` returns a cleanup function, it will be called when the effect is destroyed.
If `subscribe` is called in multiple effects, `start` will only be called once as long as the effects
are active, and the returned teardown function will only be called when all effects are destroyed.
It's best understood with an example. Here's an implementation of [`MediaQuery`](/docs/svelte/svelte-reactivity#MediaQuery):
```js
// @errors: 7031
import { createSubscriber } from 'svelte/reactivity';
import { on } from 'svelte/events';
export class MediaQuery {
#query;
#subscribe;
constructor(query) {
this.#query = window.matchMedia(`(${query})`);
this.#subscribe = createSubscriber((update) => {
// when the `change` event occurs, re-run any effects that read `this.current`
const off = on(this.#query, 'change', update);
// stop listening when all the effects are destroyed
return () => off();
});
}
get current() {
// This makes the getter reactive, if read in an effect
this.#subscribe();
// Return the current state of the query, whether or not we're in an effect
return this.#query.matches;
}
}
```
<div class="ts-block">
```dts
function createSubscriber(
start: (update: () => void) => (() => void) | void
): () => void;
```
</div>

27
pnpm-lock.yaml generated
View File

@@ -123,8 +123,8 @@ catalogs:
specifier: ^1.5.0
version: 1.5.0
'@vercel/analytics':
specifier: ^1.5.0
version: 1.6.1
specifier: ^2.0.0
version: 2.0.1
dotenv:
specifier: ^17.2.3
version: 17.2.3
@@ -192,7 +192,7 @@ importers:
version: 10.1.8(eslint@9.39.2)
eslint-plugin-import:
specifier: catalog:lint
version: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)
version: 2.32.0(eslint@9.39.2)
eslint-plugin-pnpm:
specifier: catalog:lint
version: 1.5.0(eslint@9.39.2)
@@ -237,7 +237,7 @@ importers:
version: 0.8.4(tmcp@1.19.2(typescript@5.9.3))
'@vercel/analytics':
specifier: catalog:tooling
version: 1.6.1(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.4)(vite@7.3.1(@types/node@24.10.9)(yaml@2.8.2)))(svelte@5.48.4)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.9)(yaml@2.8.2)))(react@18.3.1)(svelte@5.48.4)
version: 2.0.1(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.4)(vite@7.3.1(@types/node@24.10.9)(yaml@2.8.2)))(svelte@5.48.4)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.9)(yaml@2.8.2)))(react@18.3.1)(svelte@5.48.4)
tmcp:
specifier: catalog:tmcp
version: 1.19.2(typescript@5.9.3)
@@ -2171,12 +2171,13 @@ packages:
peerDependencies:
valibot: ^1.2.0
'@vercel/analytics@1.6.1':
resolution: {integrity: sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg==}
'@vercel/analytics@2.0.1':
resolution: {integrity: sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==}
peerDependencies:
'@remix-run/react': ^2
'@sveltejs/kit': ^1 || ^2
next: '>= 13'
nuxt: '>= 3'
react: ^18 || ^19 || ^19.0.0-rc
svelte: '>= 4'
vue: ^3
@@ -2188,6 +2189,8 @@ packages:
optional: true
next:
optional: true
nuxt:
optional: true
react:
optional: true
svelte:
@@ -4179,6 +4182,7 @@ packages:
tar@7.5.7:
resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==}
engines: {node: '>=18'}
deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
term-size@2.2.1:
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
@@ -6169,7 +6173,7 @@ snapshots:
dependencies:
valibot: 1.2.0(typescript@5.9.3)
'@vercel/analytics@1.6.1(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.4)(vite@7.3.1(@types/node@24.10.9)(yaml@2.8.2)))(svelte@5.48.4)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.9)(yaml@2.8.2)))(react@18.3.1)(svelte@5.48.4)':
'@vercel/analytics@2.0.1(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.4)(vite@7.3.1(@types/node@24.10.9)(yaml@2.8.2)))(svelte@5.48.4)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.9)(yaml@2.8.2)))(react@18.3.1)(svelte@5.48.4)':
optionalDependencies:
'@sveltejs/kit': 2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.4)(vite@7.3.1(@types/node@24.10.9)(yaml@2.8.2)))(svelte@5.48.4)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.9)(yaml@2.8.2))
react: 18.3.1
@@ -6842,17 +6846,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2):
eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint@9.39.2):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3)
eslint: 9.39.2
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2):
eslint-plugin-import@2.32.0(eslint@9.39.2):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -6863,7 +6866,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.39.2
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2)
eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint@9.39.2)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -6874,8 +6877,6 @@ snapshots:
semver: 6.3.1
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack

View File

@@ -46,7 +46,7 @@ catalogs:
'@types/estree': ^1.0.8
'@types/node': ^24.3.1
'@valibot/to-json-schema': ^1.5.0
'@vercel/analytics': ^1.5.0
'@vercel/analytics': ^2.0.0
dotenv: ^17.2.3
node-resolve-ts: ^1.0.2
publint: ^0.3.13

View File

@@ -0,0 +1,54 @@
import fs from 'node:fs/promises';
import { execSync } from 'node:child_process';
interface PluginJson {
name: string;
version: string;
[key: string]: unknown;
}
const PLUGINS = [
{
name: 'Claude',
diff_path: 'plugins/claude/svelte/',
json_path: './plugins/claude/svelte/.claude-plugin/plugin.json',
},
{
name: 'Cursor',
diff_path: 'plugins/cursor/svelte/',
json_path: './plugins/cursor/svelte/.cursor-plugin/plugin.json',
},
];
function has_changes(diff_path: string): boolean {
try {
execSync(`git diff --exit-code ${diff_path}`, { stdio: 'pipe' });
return false;
} catch {
return true;
}
}
function bump_patch(version: string): string {
const parts = version.split('.');
if (parts.length !== 3) {
throw new Error(`Invalid semver: ${version}`);
}
const [major, minor, patch] = parts;
return `${major}.${minor}.${Number(patch) + 1}`;
}
for (const plugin of PLUGINS) {
if (!has_changes(plugin.diff_path)) {
console.log(`No changes in ${plugin.name} plugin, skipping version bump`);
continue;
}
const raw = await fs.readFile(plugin.json_path, 'utf-8');
const json: PluginJson = JSON.parse(raw);
const old_version = json.version;
json.version = bump_patch(old_version);
await fs.writeFile(plugin.json_path, JSON.stringify(json, null, '\t') + '\n');
console.log(`Bumped ${plugin.name} plugin: ${old_version} -> ${json.version}`);
}

View File

@@ -0,0 +1,276 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { parseArgs } from 'node:util';
const { values } = parseArgs({
options: {
file: { type: 'string', short: 'f' },
repo: { type: 'string', short: 'r' },
output: { type: 'string', short: 'o' },
},
});
const { file, repo, output } = values;
if (!file || !repo || !output) {
console.error(
'Usage: resolve-references --file <path-or-content> --repo <repo> --output <folder>',
);
process.exit(1);
}
export function remove_llm_ignore_blocks(content: string): string {
return content.replace(/<!--\s*llm-ignore-start\s*-->[\s\S]*?<!--\s*llm-ignore-end\s*-->/g, '');
}
/**
* Determines whether the input string is a file path or raw markdown content.
* If it's a file, reads and returns its content. Otherwise returns the string as-is.
*/
async function get_content(input: string) {
try {
const stat = await fs.stat(input);
if (stat.isFile()) {
return await fs.readFile(input, 'utf-8');
}
} catch {
// not a file path — treat as raw content
}
return input;
}
/**
* Extracts a section from markdown content based on a heading id (hash).
* Finds the heading whose text (lowercased, spaces replaced with `-`) matches
* the hash and returns everything from that heading up to the next heading of
* the same or higher level.
*/
function extract_section(content: string, hash: string) {
const lines = content.split('\n');
let start_index = -1;
let heading_level = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i]!;
const heading_match = line.match(/^(#{1,6})\s+(.+)/);
if (!heading_match) continue;
const level = heading_match[1]!.length;
const text = heading_match[2]!;
const slug = text.toLowerCase().replace(/\s+/g, '-');
if (slug === hash.toLowerCase()) {
start_index = i;
heading_level = level;
continue;
}
if (start_index !== -1 && level <= heading_level) {
return lines.slice(start_index, i).join('\n').trim();
}
}
if (start_index !== -1) {
return lines.slice(start_index).join('\n').trim();
}
return content;
}
/**
* Removes the `title`, `skill`, and `NOTE` fields from markdown frontmatter, if present.
* Removes the entire frontmatter block if they were the only fields.
*/
function remove_frontmatter_unneeded_fields(content: string) {
const frontmatter_match = content.match(/^---\n([\s\S]*?)\n---\n?/);
if (!frontmatter_match) return content;
const frontmatter = frontmatter_match[1]!;
const lines = frontmatter.split('\n').filter((line) => !line.match(/^(title|skill|NOTE)\s*:/));
if (lines.length === 0) {
// frontmatter is now empty — remove the whole block
return content.slice(frontmatter_match[0].length);
}
return `---\n${lines.join('\n')}\n---\n` + content.slice(frontmatter_match[0].length);
}
/**
* Derives a file-safe name from a URL path segment.
* e.g. "some/deep/path" -> "path"
*/
function derive_name(link: string) {
const without_hash = link.split('#')[0]!;
const segments = without_hash.split('/').filter(Boolean);
return segments[segments.length - 1] ?? 'reference';
}
const content = remove_llm_ignore_blocks(
remove_frontmatter_unneeded_fields(await get_content(file)),
);
// Match markdown links that are either:
// 1. Relative paths (not starting with http://, https://, mailto:, #, or /)
// 2. Absolute /docs/ paths (e.g. /docs/svelte/each)
const relative_link_regex = /\[([^\]]*)\]\((?!https?:\/\/|mailto:|#|\/)([^)]+)\)/g;
const docs_link_regex = /\[([^\]]*)\]\((\/docs\/[^)]+)\)/g;
interface Link_Info {
full_match: string;
text: string;
href: string;
hash: string | undefined;
clean_path: string;
is_absolute_docs: boolean;
}
const links: Link_Info[] = [];
let match;
while ((match = relative_link_regex.exec(content)) !== null) {
const href = match[2]!;
const hash_index = href.indexOf('#');
const has_hash = hash_index !== -1;
links.push({
full_match: match[0],
text: match[1]!,
href,
hash: has_hash ? href.slice(hash_index + 1) : undefined,
clean_path: has_hash ? href.slice(0, hash_index) : href,
is_absolute_docs: false,
});
}
while ((match = docs_link_regex.exec(content)) !== null) {
const href = match[2]!;
const hash_index = href.indexOf('#');
const has_hash = hash_index !== -1;
links.push({
full_match: match[0],
text: match[1]!,
href,
hash: has_hash ? href.slice(hash_index + 1) : undefined,
clean_path: has_hash ? href.slice(0, hash_index) : href,
is_absolute_docs: true,
});
}
if (links.length === 0) {
console.log('No relative links found in the markdown.');
process.exit(0);
}
console.log(`Found ${links.length} relative link(s) to resolve.`);
const references_dir = path.join(output, 'references');
await fs.mkdir(references_dir, { recursive: true });
let updated_content = content;
// Track names we've already used to avoid collisions
const used_names = new Map<string, number>();
for (const link of links) {
const base_name = derive_name(link.clean_path);
const count = used_names.get(base_name) ?? 0;
used_names.set(base_name, count + 1);
const name = count > 0 ? `${base_name}-${count}` : base_name;
// For absolute /docs/ links, fetch directly from svelte.dev (supports cross-repo links).
// For relative links, prepend /docs/{repo}/.
const url = link.is_absolute_docs
? `https://svelte.dev${link.clean_path}/llms.txt`
: `https://svelte.dev/docs/${repo}/${link.clean_path}/llms.txt`;
console.log(`Fetching: ${url}${link.hash ? ` (section: #${link.hash})` : ''}`);
try {
const response = await fetch(url);
if (!response.ok) {
console.warn(` Warning: ${response.status} ${response.statusText} for ${url}`);
continue;
}
let fetched_content = await response.text();
if (link.hash) {
fetched_content = extract_section(fetched_content, link.hash);
}
const ref_filename = `${name}.md`;
const ref_path = path.join(references_dir, ref_filename);
await fs.writeFile(ref_path, remove_llm_ignore_blocks(remove_cut_preambles(fetched_content)));
console.log(` Saved: references/${ref_filename}`);
// Replace the link in the markdown
const new_link = `[${link.text}](references/${ref_filename})`;
updated_content = updated_content.replace(link.full_match, new_link);
} catch (error) {
console.warn(` Error fetching ${url}:`, error);
}
}
/**
* In fenced code blocks, removes everything from the start of the block
* up to and including a `// ---cut---` comment. If no such comment exists
* the code block is left unchanged.
*/
function remove_cut_preambles(content: string) {
const lines = content.split('\n');
const result: string[] = [];
let in_code_block = false;
let code_block_buffer: string[] = [];
let fence_line = '';
for (const line of lines) {
if (!in_code_block && line.match(/^```\w*$/)) {
in_code_block = true;
fence_line = line;
code_block_buffer = [];
continue;
}
if (in_code_block && line.match(/^```$/)) {
// End of code block — check if there was a cut comment
const cut_index = code_block_buffer.findIndex((l) => l.match(/^\s*\/\/\s*---cut---\s*$/));
result.push(fence_line);
if (cut_index !== -1) {
result.push(...code_block_buffer.slice(cut_index + 1));
} else {
result.push(...code_block_buffer);
}
result.push(line);
in_code_block = false;
code_block_buffer = [];
continue;
}
if (in_code_block) {
code_block_buffer.push(line);
} else {
result.push(line);
}
}
// If file ends mid-code-block, flush as-is
if (in_code_block) {
result.push(fence_line);
result.push(...code_block_buffer);
}
return result.join('\n');
}
// Write the updated markdown content to the output folder
updated_content = remove_cut_preambles(updated_content);
const output_filename = path.join(output, 'SKILL.md');
await fs.writeFile(output_filename, updated_content);
console.log(`\nUpdated markdown written to: ${output_filename}`);

View File

@@ -0,0 +1,55 @@
import fs from 'node:fs/promises';
import path from 'node:path';
const CLAUDE_PLUGIN_DIR = './plugins/claude/svelte';
const TOOLS_DIR = './tools';
/**
* Sync skills from tools/ to Claude plugin (direct copy)
*/
async function sync_skills() {
const source = path.join(TOOLS_DIR, 'skills');
const dest = path.join(CLAUDE_PLUGIN_DIR, 'skills');
await fs.rm(dest, { recursive: true, force: true });
await fs.cp(source, dest, { recursive: true });
console.log('Synced skills to Claude plugin');
}
/**
* Sync agent definitions from tools/ to Claude plugin,
* adding Claude-specific frontmatter fields (permissionMode: acceptEdits)
*/
async function sync_agents() {
const source = path.join(TOOLS_DIR, 'agents');
const dest = path.join(CLAUDE_PLUGIN_DIR, 'agents');
await fs.rm(dest, { recursive: true, force: true });
await fs.mkdir(dest, { recursive: true });
const files = await fs.readdir(source);
for (const file of files) {
if (!file.endsWith('.md')) continue;
const content = await fs.readFile(path.join(source, file), 'utf-8');
// Add Claude-specific frontmatter fields
const transformed = content.replace(
/^(---\n)([\s\S]*?)(---\n)/m,
(_match, open, frontmatter, close) => {
return `${open}${(frontmatter as string).trimEnd()}\npermissionMode: acceptEdits\n${close}`;
},
);
await fs.writeFile(path.join(dest, file), transformed);
}
console.log('Synced agents to Claude plugin');
}
await sync_skills();
await sync_agents();
console.log('Claude plugin sync complete');

View File

@@ -0,0 +1,85 @@
import fs from 'node:fs/promises';
import path from 'node:path';
const CURSOR_PLUGIN_DIR = './plugins/cursor/svelte';
const TOOLS_PLUGIN_DIR = './tools';
const AGENTS_MD_PATH = './tools/instructions/AGENTS.md';
/**
* Sync skills from Claude plugin to Cursor plugin (direct copy)
*/
async function sync_skills() {
const source = path.join(TOOLS_PLUGIN_DIR, 'skills');
const dest = path.join(CURSOR_PLUGIN_DIR, 'skills');
await fs.rm(dest, { recursive: true, force: true });
await fs.cp(source, dest, { recursive: true });
console.log('Synced skills to Cursor plugin');
}
/**
* Sync agent definition from Claude plugin to Cursor plugin,
* stripping Claude-specific frontmatter fields (permissionMode)
*/
async function sync_agents() {
const source = path.join(TOOLS_PLUGIN_DIR, 'agents');
const dest = path.join(CURSOR_PLUGIN_DIR, 'agents');
await fs.rm(dest, { recursive: true, force: true });
await fs.mkdir(dest, { recursive: true });
const files = await fs.readdir(source);
for (const file of files) {
if (!file.endsWith('.md')) continue;
const content = await fs.readFile(path.join(source, file), 'utf-8');
// Strip Claude-specific frontmatter fields
const transformed = content.replace(
/^(---\n)([\s\S]*?)(---\n)/m,
(_match, open, frontmatter, close) => {
const filtered_lines = (frontmatter as string)
.split('\n')
.filter((line: string) => !line.startsWith('permissionMode:'))
.join('\n');
return `${open}${filtered_lines}${close}`;
},
);
await fs.writeFile(path.join(dest, file), transformed);
}
console.log('Synced agents to Cursor plugin');
}
/**
* Sync AGENTS.md instructions as a Cursor rule (.mdc file with frontmatter)
*/
async function sync_rules() {
const dest = path.join(CURSOR_PLUGIN_DIR, 'rules');
await fs.rm(dest, { recursive: true, force: true });
await fs.mkdir(dest, { recursive: true });
const agents_content = await fs.readFile(AGENTS_MD_PATH, 'utf-8');
const rule_content = `---
description: Instructions for using the Svelte MCP server tools for documentation lookup, code analysis, and validation
alwaysApply: true
---
${agents_content.trim()}
`;
await fs.writeFile(path.join(dest, 'svelte-mcp-tools.mdc'), rule_content);
console.log('Synced rules to Cursor plugin');
}
await sync_skills();
await sync_agents();
await sync_rules();
console.log('Cursor plugin sync complete');

View File

@@ -0,0 +1,113 @@
import fs from 'node:fs/promises';
import path from 'node:path';
const OPENCODE_PKG_DIR = './packages/opencode';
const TOOLS_DIR = './tools';
const DOCS_AGENTS_DIR = './documentation/docs/10-introduction/.generated';
/**
* Sync skills from tools/ to opencode package (direct copy)
*/
async function sync_skills() {
const source = path.join(TOOLS_DIR, 'skills');
const dest = path.join(OPENCODE_PKG_DIR, 'skills');
await fs.rm(dest, { recursive: true, force: true });
await fs.cp(source, dest, { recursive: true });
console.log('Synced skills to opencode package');
}
/**
* Sync AGENTS.md from tools/ to opencode package and documentation site
*/
async function sync_agents_md() {
const source = path.join(TOOLS_DIR, 'instructions', 'AGENTS.md');
const opencode_dest = path.join(OPENCODE_PKG_DIR, 'instructions', 'opencode-agents.md');
const docs_dest = path.join(DOCS_AGENTS_DIR, 'agents.md');
await fs.mkdir(path.dirname(opencode_dest), { recursive: true });
await fs.mkdir(DOCS_AGENTS_DIR, { recursive: true });
const content = await fs.readFile(source, 'utf-8');
await fs.writeFile(opencode_dest, content);
await fs.writeFile(docs_dest, content);
console.log('Synced AGENTS.md to opencode package and documentation');
}
interface AgentData {
name: string;
description: string;
prompt: string;
}
/**
* Parse a markdown agent file with frontmatter (name, description) and body (prompt)
*/
function parse_agent_md(content: string, file_path: string): AgentData | null {
const frontmatter_match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
if (!frontmatter_match) {
console.warn(`Skipping ${file_path}: no frontmatter found`);
return null;
}
const frontmatter = frontmatter_match[1] ?? '';
const body = (frontmatter_match[2] ?? '').trim();
const name = frontmatter.match(/^name:\s*(.+)$/m)?.[1]?.trim();
const description = frontmatter.match(/^description:\s*(.+)$/m)?.[1]?.trim();
if (!name || !description) {
console.warn(`Skipping ${file_path}: missing name or description in frontmatter`);
return null;
}
return { name, description, prompt: body };
}
/**
* Generate agents.ts module from tools/agents/*.md files
*/
async function sync_agents() {
const agents_dir = path.join(TOOLS_DIR, 'agents');
const entries = await fs.readdir(agents_dir, { withFileTypes: true });
const md_files = entries.filter((e) => e.isFile() && e.name.endsWith('.md'));
const agents: AgentData[] = [];
for (const file of md_files) {
const file_path = path.join(agents_dir, file.name);
const content = await fs.readFile(file_path, 'utf-8');
const agent = parse_agent_md(content, file_path);
if (agent) {
agents.push(agent);
}
}
const output = [
'// This file is auto-generated by scripts/sync-opencode-plugin.ts',
'// Do not edit manually — edit the markdown files in tools/agents/ instead.',
'',
`export const agents = ${JSON.stringify(
Object.fromEntries(
agents.map((a) => [a.name, { description: a.description, prompt: a.prompt }]),
),
null,
'\t',
)} as const;`,
'',
].join('\n');
const dest = path.join(OPENCODE_PKG_DIR, 'agents.ts');
await fs.writeFile(dest, output);
console.log(`Generated agents.ts with ${agents.length} agent(s)`);
}
await sync_skills();
await sync_agents_md();
await sync_agents();
console.log('OpenCode plugin sync complete');

View File

@@ -1,13 +1,7 @@
import fs from 'node:fs/promises';
import path from 'node:path';
let content = `---
title: Prompts
---
This is the list of available prompts provided by the MCP server. Prompts are selected by the user and are sent as a user message. They can be useful to write repetitive instructions for the LLM on how to properly use the MCP server.
`;
let content = '';
const prompts_generators = fs.glob('./packages/mcp-server/src/mcp/handlers/prompts/*.ts');
@@ -24,13 +18,17 @@ ${module.docs_description}
<details>
<summary>Copy the prompt</summary>
\`\`\`md
<!-- prettier-ignore-start -->
\`\`\`\`markdown
${await module.generate_for_docs()}
\`\`\`
\`\`\`\`
<!-- prettier-ignore-end -->
</details>
`;
}
await fs.writeFile('./documentation/docs/30-capabilities/30-prompts.md', content.trim() + '\n');
const generated_dir = './documentation/docs/30-capabilities/.generated';
await fs.mkdir(generated_dir, { recursive: true });
await fs.writeFile(path.join(generated_dir, 'prompts.md'), content.trim() + '\n');

View File

@@ -44,19 +44,9 @@ function parse_frontmatter(
};
}
let content = `---
title: Overview
---
let content = '';
This is the list of available skills provided by the Svelte MCP package. Skills are sets of instructions that AI agents can load on-demand to help with specific tasks.
Skills are available in both the Claude Code plugin (installed via the marketplace) and the OpenCode plugin (\`@sveltejs/opencode\`). They can also be manually installed in your \`.claude/skills/\` or \`.opencode/skills/\` folder.
You can download the latest skills from the [releases page](https://github.com/sveltejs/mcp/releases) or find them in the [\`plugins/svelte/skills\`](https://github.com/sveltejs/mcp/tree/main/plugins/svelte/skills) folder.
`;
const skills_dir = './plugins/svelte/skills';
const skills_dir = './tools/skills';
const skill_dirs = (await fs.readdir(skills_dir)).filter((name) => !name.startsWith('.'));
for (const skill_name of skill_dirs) {
@@ -78,7 +68,7 @@ for (const skill_name of skill_dirs) {
${frontmatter.description}
<a href="https://github.com/sveltejs/mcp/releases?q=${frontmatter.name}" target="_blank" rel="noopener noreferrer">Open Releases page</a>
<a href="https://github.com/sveltejs/ai-tools/releases?q=${frontmatter.name}" target="_blank" rel="noopener noreferrer">Open Releases page</a>
<details>
<summary>View skill content</summary>
@@ -97,4 +87,6 @@ ${fence}
}
}
await fs.writeFile('./documentation/docs/60-skills/10-skills.md', content.trim() + '\n');
const generated_dir = './documentation/docs/60-skills/.generated';
await fs.mkdir(generated_dir, { recursive: true });
await fs.writeFile(path.join(generated_dir, 'skills.md'), content.trim() + '\n');

View File

@@ -0,0 +1,67 @@
---
name: svelte-file-editor
description: Specialized Svelte 5 code editor. MUST BE USED PROACTIVELY when creating, editing, or reviewing any .svelte file or .svelte.ts/.svelte.js module and MUST use the tools from the MCP server or the `svelte-file-editor` skill if they are available. Fetches relevant documentation and validates code using the Svelte MCP server tools.
---
You are a Svelte 5 expert responsible for writing, editing, and validating Svelte components and modules. You have access to the Svelte MCP server which provides documentation and code analysis tools. Always use the tools from the svelte MCP server to fetch documentation with `get_documentation` and validating the code with `svelte_autofixer`. If the autofixer returns any issue or suggestions try to solve them.
If the MCP tools are not available you can use the `svelte-code-writer` skill to learn how to use the `@sveltejs/mcp` cli to access the same tools.
If the skill is not available you can run `npx @sveltejs/mcp@latest -y --help` to learn how to use it.
## Available MCP Tools
### 1. list-sections
Lists all available Svelte 5 and SvelteKit documentation sections with titles and paths. Use this first to discover what documentation is available.
### 2. get-documentation
Retrieves full documentation for specified sections. Accepts a single section name or an array of section names. Use after `list-sections` to fetch relevant docs for the task at hand.
**Example sections:** `$state`, `$derived`, `$effect`, `$props`, `$bindable`, `snippets`, `routing`, `load functions`
### 3. svelte-autofixer
Analyzes Svelte code and returns suggestions to fix issues. Pass the component code directly to this tool. It will detect common mistakes like:
- Using `$effect` instead of `$derived` for computations
- Missing cleanup in effects
- Svelte 4 syntax (`on:click`, `export let`, `<slot>`)
- Missing keys in `{#each}` blocks
- And more
## Workflow
When invoked to work on a Svelte file:
### 1. Gather Context (if needed)
If you're uncertain about Svelte 5 syntax or patterns, use the MCP tools:
1. Call `list-sections` to see available documentation
2. Call `get-documentation` with relevant section names
### 2. Read the Target File
Read the file to understand the current implementation.
### 3. Make Changes
Apply edits following Svelte 5 best practices:
### 4. Validate Changes
After editing, ALWAYS call `svelte-autofixer` with the updated code to check for issues.
### 5. Fix Any Issues
If the autofixer reports problems, fix them and re-validate until no issues remain.
## Output Format
After completing your work, provide:
1. Summary of changes made
2. Any issues found and fixed by the autofixer
3. Recommendations for further improvements (if any)

View File

@@ -0,0 +1,23 @@
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available Svelte MCP Tools:
### 1. list-sections
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
### 2. get-documentation
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
### 3. svelte-autofixer
Analyzes Svelte code and returns issues and suggestions.
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
### 4. playground-link
Generates a Svelte Playground link with the provided code.
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.

View File

@@ -0,0 +1,66 @@
---
name: svelte-code-writer
description: CLI tools for Svelte 5 documentation lookup and code analysis. MUST be used whenever creating, editing or analyzing any Svelte component (.svelte) or Svelte module (.svelte.ts/.svelte.js). If possible, this skill should be executed within the svelte-file-editor agent for optimal results.
---
# Svelte 5 Code Writer
## CLI Tools
You have access to `@sveltejs/mcp` CLI for Svelte-specific assistance. Use these commands via `npx`:
### List Documentation Sections
```bash
npx @sveltejs/mcp list-sections
```
Lists all available Svelte 5 and SvelteKit documentation sections with titles and paths.
### Get Documentation
```bash
npx @sveltejs/mcp get-documentation "<section1>,<section2>,..."
```
Retrieves full documentation for specified sections. Use after `list-sections` to fetch relevant docs.
**Example:**
```bash
npx @sveltejs/mcp get-documentation "$state,$derived,$effect"
```
### Svelte Autofixer
```bash
npx @sveltejs/mcp svelte-autofixer "<code_or_path>" [options]
```
Analyzes Svelte code and suggests fixes for common issues.
**Options:**
- `--async` - Enable async Svelte mode (default: false)
- `--svelte-version` - Target version: 4 or 5 (default: 5)
**Examples:**
```bash
# Analyze inline code (escape $ as \$)
npx @sveltejs/mcp svelte-autofixer '<script>let count = \$state(0);</script>'
# Analyze a file
npx @sveltejs/mcp svelte-autofixer ./src/lib/Component.svelte
# Target Svelte 4
npx @sveltejs/mcp svelte-autofixer ./Component.svelte --svelte-version 4
```
**Important:** When passing code with runes (`$state`, `$derived`, etc.) via the terminal, escape the `$` character as `\$` to prevent shell variable substitution.
## Workflow
1. **Uncertain about syntax?** Run `list-sections` then `get-documentation` for relevant topics
2. **Reviewing/debugging?** Run `svelte-autofixer` on the code to detect issues
3. **Always validate** - Run `svelte-autofixer` before finalizing any Svelte component

View File

@@ -0,0 +1,176 @@
---
name: svelte-core-bestpractices
description: Guidance on writing fast, robust, modern Svelte code. Load this skill whenever in a Svelte project and asked to write/edit or analyze a Svelte component or module. Covers reactivity, event handling, styling, integration with libraries and more.
---
## `$state`
Only use the `$state` rune for variables that should be _reactive_ — in other words, variables that cause an `$effect`, `$derived` or template expression to update. Everything else can be a normal variable.
Objects and arrays (`$state({...})` or `$state([...])`) are made deeply reactive, meaning mutation will trigger updates. This has a trade-off: in exchange for fine-grained reactivity, the objects must be proxied, which has performance overhead. In cases where you're dealing with large objects that are only ever reassigned (rather than mutated), use `$state.raw` instead. This is often the case with API responses, for example.
## `$derived`
To compute something from state, use `$derived` rather than `$effect`:
```js
// do this
let square = $derived(num * num);
// don't do this
let square;
$effect(() => {
square = num * num;
});
```
> [!NOTE] `$derived` is given an expression, _not_ a function. If you need to use a function (because the expression is complex, for example) use `$derived.by`.
Deriveds are writable — you can assign to them, just like `$state`, except that they will re-evaluate when their expression changes.
If the derived expression is an object or array, it will be returned as-is — it is _not_ made deeply reactive. You can, however, use `$state` inside `$derived.by` in the rare cases that you need this.
## `$effect`
Effects are an escape hatch and should mostly be avoided. In particular, avoid updating state inside effects.
- If you need to sync state to an external library such as D3, it is often neater to use [`{@attach ...}`](references/@attach.md)
- If you need to run some code in response to user interaction, put the code directly in an event handler or use a [function binding](references/bind.md) as appropriate
- If you need to log values for debugging purposes, use [`$inspect`](references/$inspect.md)
- If you need to observe something external to Svelte, use [`createSubscriber`](references/svelte-reactivity.md)
Never wrap the contents of an effect in `if (browser) {...}` or similar — effects do not run on the server.
## `$props`
Treat props as though they will change. For example, values that depend on props should usually use `$derived`:
```js
// @errors: 2451
let { type } = $props();
// do this
let color = $derived(type === 'danger' ? 'red' : 'green');
// don't do this — `color` will not update if `type` changes
let color = type === 'danger' ? 'red' : 'green';
```
## `$inspect.trace`
`$inspect.trace` is a debugging tool for reactivity. If something is not updating properly or running more than it should you can add `$inspect.trace(label)` as the first line of an `$effect` or `$derived.by` (or any function they call) to trace their dependencies and discover which one triggered an update.
## Events
Any element attribute starting with `on` is treated as an event listener:
```svelte
<button onclick={() => {...}}>click me</button>
<!-- attribute shorthand also works -->
<button {onclick}>...</button>
<!-- so do spread attributes -->
<button {...props}>...</button>
```
If you need to attach listeners to `window` or `document` you can use `<svelte:window>` and `<svelte:document>`:
```svelte
<svelte:window onkeydown={...} />
<svelte:document onvisibilitychange={...} />
```
Avoid using `onMount` or `$effect` for this.
## Snippets
[Snippets](references/snippet.md) are a way to define reusable chunks of markup that can be instantiated with the [`{@render ...}`](references/@render.md) tag, or passed to components as props. They must be declared within the template.
```svelte
{#snippet greeting(name)}
<p>hello {name}!</p>
{/snippet}
{@render greeting('world')}
```
> [!NOTE] Snippets declared at the top level of a component (i.e. not inside elements or blocks) can be referenced inside `<script>`. A snippet that doesn't reference component state is also available in a `<script module>`, in which case it can be exported for use by other components.
## Each blocks
Prefer to use [keyed each blocks](references/each.md) — this improves performance by allowing Svelte to surgically insert or remove items rather than updating the DOM belonging to existing items.
> [!NOTE] The key _must_ uniquely identify the object. Do not use the index as a key.
Avoid destructuring if you need to mutate the item (with something like `bind:value={item.count}`, for example).
## Using JavaScript variables in CSS
If you have a JS variable that you want to use inside CSS you can set a CSS custom property with the `style:` directive.
```svelte
<div style:--columns={columns}>...</div>
```
You can then reference `var(--columns)` inside the component's `<style>`.
## Styling child components
The CSS in a component's `<style>` is scoped to that component. If a parent component needs to control the child's styles, the preferred way is to use CSS custom properties:
```svelte
<!-- Parent.svelte -->
<Child --color="red" />
<!-- Child.svelte -->
<h1>Hello</h1>
<style>
h1 {
color: var(--color);
}
</style>
```
If this is impossible (for example, the child component comes from a library) you can use `:global` to override styles:
```svelte
<div>
<Child />
</div>
<style>
div :global {
h1 {
color: red;
}
}
</style>
```
## Context
Consider using context instead of declaring state in a shared module. This will scope the state to the part of the app that needs it, and eliminate the possibility of it leaking between users when server-side rendering.
Use `createContext` rather than `setContext` and `getContext`, as it provides type safety.
## Async Svelte
If using version 5.36 or higher, you can use [await expressions](references/await-expressions.md) and [hydratable](references/hydratable.md) to use promises directly inside components. Note that these require the `experimental.async` option to be enabled in `svelte.config.js` as they are not yet considered fully stable.
## Avoid legacy features
Always use runes mode for new code, and avoid features that have more modern replacements:
- use `$state` instead of implicit reactivity (e.g. `let count = 0; count += 1`)
- use `$derived` and `$effect` instead of `$:` assignments and statements (but only use effects when there is no better solution)
- use `$props` instead of `export let`, `$$props` and `$$restProps`
- use `onclick={...}` instead of `on:click={...}`
- use `{#snippet ...}` and `{@render ...}` instead of `<slot>` and `$$slots` and `<svelte:fragment>`
- use `<DynamicComponent>` instead of `<svelte:component this={DynamicComponent}>`
- use `import Self from './ThisComponent.svelte'` and `<Self>` instead of `<svelte:self>`
- use classes with `$state` fields to share reactivity between components, instead of using stores
- use `{@attach ...}` instead of `use:action`
- use clsx-style arrays and objects in `class` attributes, instead of the `class:` directive

View File

@@ -0,0 +1,53 @@
> [!NOTE] `$inspect` only works during development. In a production build it becomes a noop.
The `$inspect` rune is roughly equivalent to `console.log`, with the exception that it will re-run whenever its argument changes. `$inspect` tracks reactive state deeply, meaning that updating something inside an object or array using fine-grained reactivity will cause it to re-fire (demo:
```svelte
<script>
let count = $state(0);
let message = $state('hello');
$inspect(count, message); // will console.log when `count` or `message` change
</script>
<button onclick={() => count++}>Increment</button>
<input bind:value={message} />
```
On updates, a stack trace will be printed, making it easy to find the origin of a state change (unless you're in the playground, due to technical limitations).
## $inspect(...).with
`$inspect` returns a property `with`, which you can invoke with a callback, which will then be invoked instead of `console.log`. The first argument to the callback is either `"init"` or `"update"`; subsequent arguments are the values passed to `$inspect` (demo:
```svelte
<script>
let count = $state(0);
$inspect(count).with((type, count) => {
if (type === 'update') {
debugger; // or `console.trace`, or whatever you want
}
});
</script>
<button onclick={() => count++}>Increment</button>
```
## $inspect.trace(...)
This rune, added in 5.14, causes the surrounding function to be _traced_ in development. Any time the function re-runs as part of an [effect]($effect) or a [derived]($derived), information will be printed to the console about which pieces of reactive state caused the effect to fire.
```svelte
<script>
import { doSomeWork } from './elsewhere';
$effect(() => {
+++// $inspect.trace must be the first statement of a function body+++
+++$inspect.trace();+++
doSomeWork();
});
</script>
```
`$inspect.trace` takes an optional first argument which will be used as the label.

View File

@@ -0,0 +1,166 @@
Attachments are functions that run in an [effect]($effect) when an element is mounted to the DOM or when [state]($state) read inside the function updates.
Optionally, they can return a function that is called before the attachment re-runs, or after the element is later removed from the DOM.
> [!NOTE]
> Attachments are available in Svelte 5.29 and newer.
```svelte
<!--- file: App.svelte --->
<script>
/** @type {import('svelte/attachments').Attachment} */
function myAttachment(element) {
console.log(element.nodeName); // 'DIV'
return () => {
console.log('cleaning up');
};
}
</script>
<div {@attach myAttachment}>...</div>
```
An element can have any number of attachments.
## Attachment factories
A useful pattern is for a function, such as `tooltip` in this example, to _return_ an attachment (demo:
```svelte
<!--- file: App.svelte --->
<script>
import tippy from 'tippy.js';
let content = $state('Hello!');
/**
* @param {string} content
* @returns {import('svelte/attachments').Attachment}
*/
function tooltip(content) {
return (element) => {
const tooltip = tippy(element, { content });
return tooltip.destroy;
};
}
</script>
<input bind:value={content} />
<button {@attach tooltip(content)}> Hover me </button>
```
Since the `tooltip(content)` expression runs inside an [effect]($effect), the attachment will be destroyed and recreated whenever `content` changes. The same thing would happen for any state read _inside_ the attachment function when it first runs. (If this isn't what you want, see [Controlling when attachments re-run](#Controlling-when-attachments-re-run).)
## Inline attachments
Attachments can also be created inline (demo:
```svelte
<!--- file: App.svelte --->
<canvas
width={32}
height={32}
{@attach (canvas) => {
const context = canvas.getContext('2d');
$effect(() => {
context.fillStyle = color;
context.fillRect(0, 0, canvas.width, canvas.height);
});
}}
></canvas>
```
> [!NOTE]
> The nested effect runs whenever `color` changes, while the outer effect (where `canvas.getContext(...)` is called) only runs once, since it doesn't read any reactive state.
## Conditional attachments
Falsy values like `false` or `undefined` are treated as no attachment, enabling conditional usage:
```svelte
<div {@attach enabled && myAttachment}>...</div>
```
## Passing attachments to components
When used on a component, `{@attach ...}` will create a prop whose key is a [`Symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). If the component then [spreads](/tutorial/svelte/spread-props) props onto an element, the element will receive those attachments.
This allows you to create _wrapper components_ that augment elements (demo:
```svelte
<!--- file: Button.svelte --->
<script>
/** @type {import('svelte/elements').HTMLButtonAttributes} */
let { children, ...props } = $props();
</script>
<!-- `props` includes attachments -->
<button {...props}>
{@render children?.()}
</button>
```
```svelte
<!--- file: App.svelte --->
<script>
import tippy from 'tippy.js';
import Button from './Button.svelte';
let content = $state('Hello!');
/**
* @param {string} content
* @returns {import('svelte/attachments').Attachment}
*/
function tooltip(content) {
return (element) => {
const tooltip = tippy(element, { content });
return tooltip.destroy;
};
}
</script>
<input bind:value={content} />
<Button {@attach tooltip(content)}>Hover me</Button>
```
## Controlling when attachments re-run
Attachments, unlike [actions](use), are fully reactive: `{@attach foo(bar)}` will re-run on changes to `foo` _or_ `bar` (or any state read inside `foo`):
```js
// @errors: 7006 2304 2552
function foo(bar) {
return (node) => {
veryExpensiveSetupWork(node);
update(node, bar);
};
}
```
In the rare case that this is a problem (for example, if `foo` does expensive and unavoidable setup work) consider passing the data inside a function and reading it in a child effect:
```js
// @errors: 7006 2304 2552
function foo(+++getBar+++) {
return (node) => {
veryExpensiveSetupWork(node);
+++ $effect(() => {
update(node, getBar());
});+++
}
}
```
## Creating attachments programmatically
To add attachments to an object that will be spread onto a component or element, use [`createAttachmentKey`](svelte-attachments#createAttachmentKey).
## Converting actions to attachments
If you're using a library that only provides actions, you can convert them to attachments with [`fromAction`](svelte-attachments#fromAction), allowing you to (for example) use them with components.

View File

@@ -0,0 +1,35 @@
To render a [snippet](snippet), use a `{@render ...}` tag.
```svelte
{#snippet sum(a, b)}
<p>{a} + {b} = {a + b}</p>
{/snippet}
{@render sum(1, 2)}
{@render sum(3, 4)}
{@render sum(5, 6)}
```
The expression can be an identifier like `sum`, or an arbitrary JavaScript expression:
```svelte
{@render (cool ? coolSnippet : lameSnippet)()}
```
## Optional snippets
If the snippet is potentially undefined — for example, because it's an incoming prop — then you can use optional chaining to only render it when it _is_ defined:
```svelte
{@render children?.()}
```
Alternatively, use an [`{#if ...}`](if) block with an `:else` clause to render fallback content:
```svelte
{#if children}
{@render children()}
{:else}
<p>fallback content</p>
{/if}
```

View File

@@ -0,0 +1,180 @@
As of Svelte 5.36, you can use the `await` keyword inside your components in three places where it was previously unavailable:
- at the top level of your component's `<script>`
- inside `$derived(...)` declarations
- inside your markup
This feature is currently experimental, and you must opt in by adding the `experimental.async` option wherever you [configure](/docs/kit/configuration) Svelte, usually `svelte.config.js`:
```js
/// file: svelte.config.js
export default {
compilerOptions: {
experimental: {
async: true,
},
},
};
```
The experimental flag will be removed in Svelte 6.
## Synchronized updates
When an `await` expression depends on a particular piece of state, changes to that state will not be reflected in the UI until the asynchronous work has completed, so that the UI is not left in an inconsistent state. In other words, in an example like this...
```svelte
<script>
let a = $state(1);
let b = $state(2);
async function add(a, b) {
await new Promise((f) => setTimeout(f, 500)); // artificial delay
return a + b;
}
</script>
<input type="number" bind:value={a} />
<input type="number" bind:value={b} />
<p>{a} + {b} = {await add(a, b)}</p>
```
...if you increment `a`, the contents of the `<p>` will _not_ immediately update to read this —
```html
<p>2 + 2 = 3</p>
```
— instead, the text will update to `2 + 2 = 4` when `add(a, b)` resolves.
Updates can overlap — a fast update will be reflected in the UI while an earlier slow update is still ongoing.
## Concurrency
Svelte will do as much asynchronous work as it can in parallel. For example if you have two `await` expressions in your markup...
```svelte
<p>{await one()}</p><p>{await two()}</p>
```
...both functions will run at the same time, as they are independent expressions, even though they are _visually_ sequential.
This does not apply to sequential `await` expressions inside your `<script>` or inside async functions — these run like any other asynchronous JavaScript. An exception is that independent `$derived` expressions will update independently, even though they will run sequentially when they are first created:
```js
// these will run sequentially the first time,
// but will update independently
let a = $derived(await one());
let b = $derived(await two());
```
> [!NOTE] If you write code like this, expect Svelte to give you an [`await_waterfall`](runtime-warnings#Client-warnings-await_waterfall) warning
## Indicating loading states
To render placeholder UI, you can wrap content in a `<svelte:boundary>` with a [`pending`](svelte-boundary#Properties-pending) snippet. This will be shown when the boundary is first created, but not for subsequent updates, which are globally coordinated.
After the contents of a boundary have resolved for the first time and have replaced the `pending` snippet, you can detect subsequent async work with [`$effect.pending()`]($effect#$effect.pending). This is what you would use to display a "we're asynchronously validating your input" spinner next to a form field, for example.
You can also use [`settled()`](svelte#settled) to get a promise that resolves when the current update is complete:
```js
import { tick, settled } from 'svelte';
async function onclick() {
updating = true;
// without this, the change to `updating` will be
// grouped with the other changes, meaning it
// won't be reflected in the UI
await tick();
color = 'octarine';
answer = 42;
await settled();
// any updates affected by `color` or `answer`
// have now been applied
updating = false;
}
```
## Error handling
Errors in `await` expressions will bubble to the nearest [error boundary](svelte-boundary).
## Server-side rendering
Svelte supports asynchronous server-side rendering (SSR) with the `render(...)` API. To use it, simply await the return value:
```js
/// file: server.js
import { render } from 'svelte/server';
import App from './App.svelte';
const { head, body } = +++await+++ render(App);
```
> [!NOTE] If you're using a framework like SvelteKit, this is done on your behalf.
If a `<svelte:boundary>` with a `pending` snippet is encountered during SSR, that snippet will be rendered while the rest of the content is ignored. All `await` expressions encountered outside boundaries with `pending` snippets will resolve and render their contents prior to `await render(...)` returning.
> [!NOTE] In the future, we plan to add a streaming implementation that renders the content in the background.
## Forking
The [`fork(...)`](svelte#fork) API, added in 5.42, makes it possible to run `await` expressions that you _expect_ to happen in the near future. This is mainly intended for frameworks like SvelteKit to implement preloading when (for example) users signal an intent to navigate.
```svelte
<script>
import { fork } from 'svelte';
import Menu from './Menu.svelte';
let open = $state(false);
/** @type {import('svelte').Fork | null} */
let pending = null;
function preload() {
pending ??= fork(() => {
open = true;
});
}
function discard() {
pending?.discard();
pending = null;
}
</script>
<button
onfocusin={preload}
onfocusout={discard}
onpointerenter={preload}
onpointerleave={discard}
onclick={() => {
pending?.commit();
pending = null;
// in case `pending` didn't exist
// (if it did, this is a no-op)
open = true;
}}>open menu</button
>
{#if open}
<!-- any async work inside this component will start
as soon as the fork is created -->
<Menu onclose={() => (open = false)} />
{/if}
```
## Caveats
As an experimental feature, the details of how `await` is handled (and related APIs like `$effect.pending()`) are subject to breaking changes outside of a semver major release, though we intend to keep such changes to a bare minimum.
## Breaking changes
Effects run in a slightly different order when the `experimental.async` option is `true`. Specifically, _block_ effects like `{#if ...}` and `{#each ...}` now run before an `$effect.pre` or `beforeUpdate` in the same component, which means that in very rare situations.

View File

@@ -0,0 +1,16 @@
## Function bindings
You can also use `bind:property={get, set}`, where `get` and `set` are functions, allowing you to perform validation and transformation:
```svelte
<input bind:value={() => value, (v) => (value = v.toLowerCase())} />
```
In the case of readonly bindings like [dimension bindings](#Dimensions), the `get` value should be `null`:
```svelte
<div bind:clientWidth={null, redraw} bind:clientHeight={null, redraw}>...</div>
```
> [!NOTE]
> Function bindings are available in Svelte 5.9.0 and newer.

View File

@@ -0,0 +1,42 @@
## Keyed each blocks
```svelte
<!--- copy: false --->
{#each expression as name (key)}...{/each}
```
```svelte
<!--- copy: false --->
{#each expression as name, index (key)}...{/each}
```
If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to intelligently update the list when data changes by inserting, moving and deleting items, rather than adding or removing items at the end and updating the state in the middle.
The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change.
```svelte
{#each items as item (item.id)}
<li>{item.name} x {item.qty}</li>
{/each}
<!-- or with additional index value -->
{#each items as item, i (item.id)}
<li>{i + 1}: {item.name} x {item.qty}</li>
{/each}
```
You can freely use destructuring and rest patterns in each blocks.
```svelte
{#each items as { id, name, qty }, i (id)}
<li>{i + 1}: {name} x {qty}</li>
{/each}
{#each objects as { id, ...rest }}
<li><span>{id}</span><MyComponent {...rest} /></li>
{/each}
{#each items as [id, ...rest]}
<li><span>{id}</span><MyComponent values={rest} /></li>
{/each}
```

Some files were not shown because too many files have changed in this diff Show More