Svelte Vibe Code Cleanup
Your AI treated Svelte like React. The reactivity is broken, stores are everywhere, and components do too much.
At Variant Systems, we pair the right technology with the right approach to ship products that work.
Why this combination
- AI misunderstands Svelte's compile-time reactivity and writes unnecessary store subscriptions
- Writable stores used for state that should be derived or component-local
- Missing $derived and $effect runes cause stale values and broken updates
- SvelteKit load functions are skipped in favor of client-side fetching
Reactivity Pitfalls When AI Thinks in React
AI trained on React and Vue doesn’t understand Svelte’s reactivity model. Svelte is a compiler. Reactivity happens at build time, not runtime. AI doesn’t get this. It wraps values in stores that should be simple reactive $state declarations. It creates manual subscriptions where Svelte’s auto-subscription syntax handles everything. It writes $effect blocks that duplicate what $derived does automatically. The code works, but it fights the framework instead of using it.
Store overuse is the hallmark of AI-generated Svelte. Every piece of state becomes a writable store. A modal’s open state. A form’s input values. A counter that only one component reads. AI uses stores because that’s the pattern it knows from other frameworks. In Svelte, most of this state should live in the component with $state. Stores are for state shared across unrelated component trees. When everything’s a store, you lose Svelte’s biggest advantage: simplicity.
Derived values get computed manually instead of reactively. AI writes $effect blocks that watch a value and update another value - which is exactly what $derived does in one line. These manual derivations are harder to read, harder to debug, and they introduce timing issues because $effect runs after the DOM update, not synchronously during the reactive update.
SvelteKit routing is misused or ignored. AI generates client-side fetch() calls in onMount instead of using SvelteKit’s load functions. Data that should be server-rendered arrives after the page loads. SEO suffers because crawlers see empty pages. Layout data that should be shared via +layout.server.ts gets fetched redundantly in every child route. The file-based routing system that SvelteKit provides goes underutilized.
Restoring Compile-Time Reactivity and Server-Side Data Loading
We audit every reactive declaration. Each $state variable is verified to trigger updates correctly. Each $derived value is checked for proper dependency tracking. Each $effect is evaluated - does it need to exist, or should it be a $derived? We remove effects that duplicate derived computation and simplify state that was over-abstracted with stores.
Stores get rationalized. We identify every writable store and ask: does more than one component tree read this? If yes, it stays as a store. If no, the state moves into the component as $state. Stores that are read but never written from outside become derived stores. Stores that combine multiple pieces of state get split into focused, single-purpose stores.
SvelteKit’s routing system gets used properly. Data fetching moves from client-side onMount to +page.server.ts load functions. Shared data moves to +layout.server.ts. Form submissions use SvelteKit’s form actions. Error handling uses +error.svelte. The app becomes server-rendered by default, with client-side interactivity added only where needed.
Component boundaries get redrawn. We identify components that handle multiple concerns and split them. A component that manages a list and a detail view becomes two components with a shared route parameter. Props and events define clean contracts between parent and child. Slots replace prop-based content injection where templates are involved.
From Client-Side Fetching to 400ms First Paint
Before: A SvelteKit app with 25 writable stores, 15 of which are only used by a single component. Load functions are empty because all data is fetched in onMount. Eight $effect blocks that manually compute what $derived handles automatically. Components that are 300 lines with mixed concerns.
After: 6 stores for genuinely shared state. All data loads server-side through SvelteKit’s load functions. Zero unnecessary $effect blocks. Components under 100 lines with clear, single responsibilities. First contentful paint drops from 2.5s to 400ms because data arrives with the HTML.
The SEO impact is immediate. Pages that returned empty <div> elements to crawlers now return fully rendered HTML with proper meta tags. SvelteKit’s server-side rendering does the work that client-side fetching was undermining.
Compiler Checks and Store Discipline That Scale
Svelte’s compiler already catches many reactivity mistakes. We configure svelte-check to run in CI with strict settings. Unused reactive declarations, missing types, and accessibility violations fail the build. The compiler becomes the first quality gate.
ESLint with eslint-plugin-svelte adds rules the compiler doesn’t cover. No writable stores in component files - stores live in dedicated files. No $effect without a comment explaining why $derived isn’t sufficient. No client-side fetching in pages that have load functions.
We establish component templates for common patterns. A list page template. A form component template. A modal template. Each uses Svelte idioms correctly - $state for local reactivity, $derived for computed values, $bindable props for two-way binding where appropriate. AI output that doesn’t match the templates gets flagged during review.
SvelteKit’s type generation ensures route safety. The $types auto-generated from load functions mean TypeScript knows exactly what data each page receives. If a load function changes its return type, every component that depends on it gets a compile error. No runtime surprises from stale data assumptions.
What you get
Ideal for
- Svelte apps where AI-generated reactivity doesn't trigger updates correctly
- Products with writable stores for everything, including local component state
- SvelteKit apps that fetch data client-side instead of using load functions
- Teams that need clean Svelte patterns before scaling the codebase