CtrlK
BlogDocsLog inGet started
Tessl Logo

svelte5

Reviews Svelte components for idiomatic patterns, identifies anti-patterns carried over from React/Vue/Svelte 4, and suggests Svelte 5 refactorings. Use when writing or reviewing Svelte components, asking "is this idiomatic", "Svelte way", doing Svelte 5 migration, or encountering smells like: effect assigns state, prop copied to $state, global store by default, $bindable everywhere, clickable div, createEventDispatcher, export let, on:click, slot-shaped APIs, lifecycle-driven code, imperative DOM wiring, immutable-update ceremony, context value replacement, shadcn-svelte form structure, Field.* components, bits-ui form controls, or component APIs that hide ownership. This is the general-purpose entry point for Svelte component review; delegates to sveltekit for routes/load/actions/server concerns and to focused references for details.

100

1.12x
Quality

100%

Does it follow best practices?

Impact

100%

1.12x

Average score across 3 eval scenarios

SecuritybySnyk

Passed

No known issues

SKILL.md
Quality
Evals
Security

Think in Svelte 5

You already know Svelte syntax. This skill changes your defaults — what you reach for first when designing a component, placing state, modeling reactivity, and reviewing UI code.

The core failure mode is Svelte that compiles but thinks like React, Vue, or Svelte 4 — effects standing in for derived values and events, props mirrored into state, global stores by reflex, two-way binding as the default, clickable divs, Svelte 4 component APIs in new code.

The component is a small reactive program with visible dependencies. Keep dataflow direct, state ownership obvious, and markup semantic; let the compiler and the DOM do the work.

This skill is the general-purpose entry point for Svelte component review; for SvelteKit-level concerns, also use sveltekit (see Cross-References).

Treat these as strong defaults, not rigid laws: when unsure, choose the approach that makes ownership, dependencies, and user semantics more explicit.

How Svelte Thinks

Make reactivity visible

1. Reactivity is read-tracked. A $derived/$effect depends on what it reads while running; if one reruns unexpectedly, inspect its reads, not a dependency array. See references/read-tracked-reactivity.md.

2. Derived state is $derived, not $effect. Pure functions of other state get derived; effects are for side effects, not keeping variables in sync. See references/effect-driven-state.md.

<!-- ❌ effect writing derived state -->
let doubled = $state(0);
$effect(() => { doubled = count * 2; });

<!-- ✅ derive it -->
let doubled = $derived(count * 2);

3. Effects are escape hatches. Before $effect, check for a derived value, an event handler, or a bind: getter/setter. Use it only to touch the outside world — subscriptions, timers, observers, imperative APIs, logging — and return cleanup.

4. Runes are statically analyzable. Compiler syntax, not runtime hooks — reusable state becomes .svelte.ts classes and factories, not custom hooks. See references/static-runes.md.

5. $state is a deep proxy, not React state. Mutate what changed in local arrays and objects; skip the immutable-update ceremony. See references/deep-state-without-immutable-ceremony.md.

Put state where ownership is clearest

6. State has an owner. Local interaction state → component; reusable behavior → .svelte.ts class; descendant-shared → context; stores only for the store contract, not every changing value. See references/state-ownership.md.

7. Do not mirror props unless you mean "draft." Copying props into $state creates two sources of truth — derive from props, or make a named editable draft with reset/commit. See references/prop-mirroring.md.

<!-- ❌ prop mirrored into local state -->
let { value } = $props();
let local = $state(value); // stale if parent changes

<!-- ✅ derive from prop -->
let { value } = $props();
let display = $derived(value.toUpperCase());

8. Context shares identity, not provider rerenders. Put a reactive object/class in context and mutate its properties; replacing plain values won't behave like React Provider updates. See references/context-identity.md.

9. Module-level mutable state is suspicious in SSR apps. In SvelteKit, exported mutable instances leak between requests — use factories, context, or request-owned data. See references/ssr-state-leaks.md.

Design component APIs around ownership

10. Props describe inputs; callbacks describe events. Component events are callback props; avoid createEventDispatcher in new code unless preserving an old public API. See references/component-patterns.md.

<!-- ❌ createEventDispatcher in Svelte 5 -->
const dispatch = createEventDispatcher();
function handleClick() { dispatch('select', item); }

<!-- ✅ callback prop -->
let { onSelect } = $props();
function handleClick() { onSelect?.(item); }

11. Two-way binding is an API commitment. $bindable means the child may mutate parent-owned state — use it for form controls and deliberate controlled components, not as a default. See references/bindable-by-default.md.

12. Snippets are render functions, not renamed slots. Use them when the parent supplies UI; type their parameters and name them by role. See references/snippets-as-render-functions.md.

13. Wrapper components should preserve the platform. Wrapping a button means accepting button attributes and keeping type, disabled, focus, and ARIA intact. See references/wrapper-components-preserve-html.md.

Let HTML and the compiler work

14. Native elements before custom behavior. A button already handles keyboard, focus, disabled, and a11y; a clickable div rebuilds the platform badly. See references/semantic-html-first.md.

15. Design-system fields should preserve form semantics. In shadcn-svelte or bits-ui forms, use Field.* for labels, groups, descriptions, and errors — not raw divs, separate Label imports, or clickable option rows. See references/shadcn-svelte-forms.md.

16. Components own markup; actions/attachments own DOM behavior. Renders UI → component. Attaches reusable imperative behavior to an element → action or attachment. See references/actions-vs-components.md.

17. Svelte 5 syntax should be consistent. New code uses $props, onclick-style attributes, snippets, and callback props; don't mix old syntax outside legacy maintenance. See references/svelte5-syntax-discipline.md.

18. Type the boundary, not every breath. Non-trivial components get a Props type; type snippets and callbacks; use svelte/elements for wrappers; avoid broad any at boundaries. See references/typescript.md.

Running a Review

  1. Detect the version first. Read package.json for the svelte major and check svelte.config.*. In a Svelte 5 project, flag Svelte 4 patterns (export let, on:event, <slot>, createEventDispatcher); in a Svelte 4 project, do not recommend Svelte 5-only syntax unless the task is migration.
  2. Apply the principles above; scan the smell index below. Open the linked reference before proposing a rewrite.
  3. Judge performance only on evidence or obvious scale. Key identified {#each}, move heavy work out of markup into $derived, avoid needless two-way bindings. Most code needs no manual optimization.
  4. Shape the output: high-impact first (correctness, reactivity, a11y, SSR leaks), then suggested rewrites (smallest useful change), then nice-to-haves. Tie every point to behavior, maintainability, or user impact — not every stylistic nit.

Code Smell Index

Scan for the smell; the principle above carries the fix.

Code smellReference
Old Svelte syntax in new codesvelte5-syntax-discipline
Effect assigns a derived valueeffect-driven-state
Effect responds to a control changingeffect-driven-state
Effect converts a bound valuebindings-and-directives
Effect reruns mysteriouslyread-tracked-reactivity
Rune called inside a helper or hookstatic-runes
Immutable-update ceremony on local statedeep-state-without-immutable-ceremony
Prop copied into local stateprop-mirroring
Global store by default / owner unclearstate-ownership
Context value replaced instead of mutatedcontext-identity
Exported mutable singleton in SvelteKitssr-state-leaks
createEventDispatcher to emit an eventcomponent-patterns
$bindable by habitbindable-by-default
Slot-shaped API (export let, <slot>)snippets-as-render-functions
Clickable div / spansemantic-html-first
shadcn-svelte form rebuilt with raw wrappersshadcn-svelte-forms
Wrapper that hides native behaviorwrapper-components-preserve-html
Action that owns markup/stateactions-vs-components
Dynamic list left unkeyedbindings-and-directives
Broad any at the component boundarytypescript
{@html} with user contenttemplate-tags
Component behavior left untestedtesting

Cross-References

  • sveltekit — Routes, layouts, load, form actions, remote functions, cookies, auth, endpoints, redirects, server errors, app-level SSR/data flow
Repository
joshuadavidthomas/agent-skills
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.