skills/svelte/svelte/SKILL.md
Svelte 5 patterns including TanStack Query mutations, shadcn-svelte components, and component composition. Use when writing Svelte components, using TanStack Query, or working with shadcn-svelte UI.
npx skillsauth add aiagentskills/skills svelteInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Always prefer createMutation from TanStack Query for mutations. This provides:
isPending)isError)isSuccess)Pass onSuccess and onError as the second argument to .mutate() to get maximum context:
<script lang="ts">
import { createMutation } from '@tanstack/svelte-query';
import * as rpc from '$lib/query';
// Create mutation with just .options (no parentheses!)
const deleteSessionMutation = createMutation(
rpc.sessions.deleteSession.options,
);
// Local state that we can access in callbacks
let isDialogOpen = $state(false);
</script>
<Button
onclick={() => {
// Pass callbacks as second argument to .mutate()
deleteSessionMutation.mutate(
{ sessionId },
{
onSuccess: () => {
// Access local state and context
isDialogOpen = false;
toast.success('Session deleted');
goto('/sessions');
},
onError: (error) => {
toast.error(error.title, { description: error.description });
},
},
);
}}
disabled={deleteSessionMutation.isPending}
>
{#if deleteSessionMutation.isPending}
Deleting...
{:else}
Delete
{/if}
</Button>
Always use .execute() since createMutation requires component context:
// In a .ts file (e.g., load function, utility)
const result = await rpc.sessions.createSession.execute({
body: { title: 'New Session' },
});
const { data, error } = result;
if (error) {
// Handle error
} else if (data) {
// Handle success
}
Only use .execute() in Svelte files when:
When a handler function only calls .mutate(), inline it directly:
<!-- Avoid: Unnecessary wrapper function -->
<script>
function handleShare() {
shareMutation.mutate({ id });
}
</script>
<!-- Good: Inline simple handlers -->
<Button onclick={() => shareMutation.mutate({ id })}>Share</Button>
<Button onclick={handleShare}>Share</Button>
For general CSS and Tailwind guidelines, see the styling skill.
bunx shadcn-svelte@latest add [component]$lib/components/ui/ with an index.ts exportdialog/, toggle-group/)Namespace imports (preferred for multi-part components):
import * as Dialog from '$lib/components/ui/dialog';
import * as ToggleGroup from '$lib/components/ui/toggle-group';
Named imports (for single components):
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
Lucide icons (always use individual imports from @lucide/svelte):
// Good: Individual icon imports
import Database from '@lucide/svelte/icons/database';
import MinusIcon from '@lucide/svelte/icons/minus';
import MoreVerticalIcon from '@lucide/svelte/icons/more-vertical';
// Bad: Don't import multiple icons from lucide-svelte
import { Database, MinusIcon, MoreVerticalIcon } from 'lucide-svelte';
The path uses kebab-case (e.g., more-vertical, minimize-2), and you can name the import whatever you want (typically PascalCase with optional Icon suffix).
cn() utility from $lib/utils for combining Tailwind classestailwind-variants for component variant systemsbackground/foreground convention for colorsUse proper component composition following shadcn-svelte patterns:
<Dialog.Root bind:open={isOpen}>
<Dialog.Trigger>
<Button>Open</Button>
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Title</Dialog.Title>
</Dialog.Header>
</Dialog.Content>
</Dialog.Root>
When building interactive components (especially with dialogs/modals), create self-contained components rather than managing state at the parent level.
<!-- Parent component -->
<script>
let deletingItem = $state(null);
function handleDelete(item) {
// delete logic
deletingItem = null;
}
</script>
{#each items as item}
<Button onclick={() => (deletingItem = item)}>Delete</Button>
{/each}
<AlertDialog open={!!deletingItem}>
<!-- Single dialog for all items -->
</AlertDialog>
<!-- DeleteItemButton.svelte -->
<script>
let { item } = $props();
let open = $state(false);
function handleDelete() {
// delete logic directly in component
}
</script>
<AlertDialog.Root bind:open>
<AlertDialog.Trigger>
<Button>Delete</Button>
</AlertDialog.Trigger>
<AlertDialog.Content>
<!-- Dialog content -->
</AlertDialog.Content>
</AlertDialog.Root>
<!-- Parent component -->
{#each items as item}
<DeleteItemButton {item} />
{/each}
The key insight: It's perfectly fine to instantiate multiple dialogs (one per row) rather than managing a single shared dialog with complex state. Modern frameworks handle this efficiently, and the code clarity is worth it.
documentation
Guides using bun.sys for system calls and file I/O in Zig. Use when implementing file operations instead of std.fs or std.posix.
development
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
development
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
development
Guides writing HMR/Dev Server tests in test/bake/. Use when creating or modifying dev server, hot reloading, or bundling tests.