Svelte is different from every other frontend framework — no virtual DOM, compiled output, reactivity that's a language feature rather than a runtime. That difference is exactly what trips up Claude Code: it defaults to React-style thinking, uses patterns that make sense in Vue or Angular but are wrong in Svelte, and ignores the compiler's superpowers entirely.
These 13 rules fix that.
Rule 1: Reactivity — no useState, no ref(), just assignment
Reactive state uses plain variable assignment. No reactive() wrapper,
no useState(), no signal(). Mutate arrays and objects directly —
Svelte's compiler tracks assignments, not mutations.
Correct: items = [...items, newItem]
Incorrect: items.push(newItem) without reassignment
Claude trained on React and Vue will reach for hooks or composables. In Svelte, let count = 0 and count++ in a click handler is the entire reactivity system.
Rule 2: Stores for cross-component state — writable, readable, derived
Cross-component state: Svelte stores (writable, readable, derived from svelte/store).
No external state library unless project explicitly requires it.
Subscribe with $ prefix in components — no manual subscribe/unsubscribe.
Store logic lives in src/lib/stores/ directory.
The $store auto-subscription syntax is a Svelte-specific feature Claude often misses. Without it, you get manual subscriptions with forgotten cleanup.
Rule 3: Lifecycle — onMount for side effects, not top-level awaits
Side effects that need the DOM: onMount().
Cleanup: return a function from onMount().
No top-level await in component script unless using SvelteKit load functions.
onDestroy() for explicit cleanup when onMount return isn't enough.
Claude sometimes puts DOM-dependent code at top-level in the script block, which runs during SSR and throws.
Rule 4: Component communication — props down, events up
Parent-to-child: export let propName (with default values where appropriate).
Child-to-parent: createEventDispatcher() and dispatch().
Sibling state: shared writable store.
No prop drilling beyond 2 levels — use store or context API.
In Svelte, export let is how you declare a prop. Claude sometimes writes this correctly, sometimes writes React-style prop destructuring. This locks it in.
Rule 5: SvelteKit routing and load functions
(If using SvelteKit)
Page data: +page.server.ts load function — never fetch in onMount for initial data.
Layout data: +layout.server.ts.
Actions: form actions in +page.server.ts — no API routes for form submissions.
API endpoints: +server.ts only for non-form data needs.
No client-side fetch for data that can be server-loaded.
SvelteKit's file-based routing and load functions are the most misunderstood part of the ecosystem. Without explicit rules, Claude writes everything in onMount.
Rule 6: Transitions and animations — use Svelte's built-ins
Entry/exit animations: svelte/transition (fade, fly, slide, scale, draw, crossfade).
Motion: svelte/motion (tweened, spring).
No external animation libraries for effects achievable with Svelte built-ins.
transition: directive on elements, not CSS class toggling.
Claude defaults to CSS classes and setTimeout for animations. Svelte's transition system is far cleaner and more performant.
Rule 7: Event handling — on:event, not addEventListener
Event handlers: on:event|modifier directive.
Modifiers: preventDefault, stopPropagation, once, passive — use the pipe syntax.
No addEventListener() in component script for DOM events on component elements.
Component events: on:custom-event directive at usage site.
Manual addEventListener in Svelte components creates cleanup bugs. The directive syntax handles cleanup automatically.
Rule 8: Slots and snippets (Svelte 5)
(If Svelte 5+)
Content projection: snippets ({#snippet name()}{/snippet}) and {@render name()}.
No slots syntax in new components — snippets replace them.
(If Svelte 4)
Content projection: <slot> with named slots for multiple regions.
Svelte 5 replaced slots with snippets. Claude's training data mixes both generations. Specify which you're using.
Rule 9: Reactive declarations — $: for derived values
Derived values from reactive state: $: derivedValue = expression.
$: statements run when their dependencies change — order matters.
No manual watchers or watch() functions.
Complex derived logic: derived() store, not $: with side effects.
$: is Svelte's reactive declaration syntax. Claude often writes imperative update logic instead of reactive declarations.
Rule 10: TypeScript — typed props and events
TypeScript: strict mode. All export let props typed explicitly.
Event dispatcher typed: createEventDispatcher<{eventName: PayloadType}>().
No implicit any on props.
Component generics (Svelte 5): use generics syntax where appropriate.
Without this, Claude generates untyped props that defeat TypeScript's benefits in Svelte.
Rule 11: SSR safety — no window/document at module level
SSR-safe code only at module level.
window, document, localStorage: only inside onMount() or browser checks.
SvelteKit: use $app/environment browser flag for conditional client code.
No direct DOM manipulation outside lifecycle functions.
The most common Svelte SSR bug: accessing window at component initialization. This rule prevents it.
Rule 12: Styling — scoped by default, global with :global()
Component styles: scoped (default). No !important for component-scoped styles.
Global styles: :global() selector or app.css only — never in component <style> without :global().
CSS custom properties for theming — no inline style objects.
Class toggling: class:name={condition} directive, not string interpolation.
The class:name directive is more readable and performant than template string class manipulation.
Rule 13: Testing — Vitest + Testing Library
Unit tests: Vitest.
Component tests: @testing-library/svelte.
No Svelte-specific test utilities that wrap the component lifecycle unnecessarily.
Test files: component.test.ts colocated with component.
Stores: test logic by importing and setting store values directly.
Without this, Claude reaches for Jest with Svelte-specific transforms that require more configuration.
The CLAUDE.md for Svelte (copy this)
# CLAUDE.md
## Stack
- Framework: Svelte 5 (or Svelte 4 — specify)
- Meta-framework: SvelteKit
- State: Svelte stores (writable/readable/derived)
- Styling: scoped CSS + CSS custom properties
- Testing: Vitest + @testing-library/svelte
## Reactivity rules
- Plain assignment for reactive state — no hooks, no refs
- $: for derived values — no manual watchers
- Mutate arrays/objects via reassignment, not mutation
- Cross-component state: writable stores with $ auto-subscription
## Component rules
- Props: export let propName — typed in TypeScript
- Events: createEventDispatcher, typed generics
- Lifecycle: onMount for DOM side effects, return cleanup function
- Transitions: svelte/transition built-ins — no external animation libs
- Events: on:event|modifier directive — no addEventListener in component script
## SvelteKit rules
- Initial data: server load functions, not onMount fetch
- Forms: form actions in +page.server.ts
- API: +server.ts for non-form endpoints only
## SSR safety
- window/document/localStorage: only inside onMount or browser checks
- No DOM access at module level
## Banned patterns
- useState(), useEffect(), ref() — wrong framework
- External state libraries unless explicitly required
- addEventListener() for component DOM events
- !important in component styles
- CSS class toggling via string interpolation (use class: directive)
Why Svelte needs stricter rules than React
React's patterns are so dominant that AI models apply them everywhere, even in frameworks where they're wrong. Svelte's reactivity system is fundamentally different — the compiler, not the runtime, handles reactivity. Without a CLAUDE.md that makes this explicit, you get React-flavored Svelte: functional, but missing the entire point of why you chose Svelte.
Part of a series: CLAUDE.md files for Go, Rust, TypeScript/Node.js, Python, Java, C#/.NET, PHP, Ruby, Elixir, Scala, Haskell, C++, Vue.js/Nuxt, React/Next.js, Flutter/Dart, Swift/iOS, Spring Boot, Django/FastAPI, Android/Jetpack Compose, NestJS, Angular, and now Svelte.
The full rules pack is at oliviacraft.lat
Top comments (0)