15 KiB
15 KiB
| 1 | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL | Applies To | Status | Verified At |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 1 | Reactivity | Use $: for reactive statements | Legacy-mode automatic dependency tracking | $: only while maintaining a legacy-mode component | Use $: in new runes-mode code | $: doubled = count * 2 | let doubled = $derived(count * 2) | Medium | https://svelte.dev/docs/svelte/legacy-reactive-assignments | svelte legacy <=4 | deprecated | 2026-08-13 |
| 3 | 2 | Reactivity | Trigger legacy reactivity with assignment | Legacy-mode reactivity tracks assignments | Reassign arrays or objects in legacy mode | Treat assignment as the Svelte 5 runes contract | items = [...items, newItem] | let items = $state([]) | High | https://svelte.dev/docs/svelte/legacy-let | svelte legacy <=4 | deprecated | 2026-08-13 |
| 4 | 3 | Reactivity | Use $state in Svelte 5 | Runes for explicit reactivity | let count = $state(0) | Implicit reactivity in Svelte 5 | let count = $state(0) | let count = 0 (Svelte 5) | Medium | https://svelte.dev/blog/runes | svelte 5 | active | 2026-08-13 |
| 5 | 4 | Reactivity | Use $derived for computed values | $derived replaces $: in Svelte 5 | let doubled = $derived(count * 2) | $: in Svelte 5 | let doubled = $derived(count * 2) | $: doubled = count * 2 (Svelte 5) | Medium | svelte 5 | active | 2026-08-13 | |
| 6 | 5 | Reactivity | Use $effect for side effects | $effect replaces $: side effects | Use $effect for subscriptions | $: for side effects in Svelte 5 | $effect(() => console.log(count)) | $: console.log(count) (Svelte 5) | Medium | svelte 5 | active | 2026-08-13 | |
| 7 | 6 | Props | Use export let for legacy props | Declare props with export let only in legacy mode | Retain export let while maintaining legacy components | Introduce export let in new runes-mode components | export let count = 0 | let { count = 0 } = $props() | High | https://svelte.dev/docs/svelte/legacy-export-let | svelte legacy <=4 | deprecated | 2026-08-13 |
| 8 | 7 | Props | Use $props in Svelte 5 | $props rune for prop access | let { name } = $props() | export let in Svelte 5 | let { name, age = 0 } = $props() | export let name; export let age = 0 | Medium | svelte 5 | active | 2026-08-13 | |
| 9 | 8 | Props | Provide prop default values | Destructure defaults from $props | Use defaults in $props destructuring | Add separate fallback mutation | let { count = 0 } = $props() | let { count } = $props(); count ??= 0 | Low | https://svelte.dev/docs/svelte/$props | svelte 5 | active | 2026-08-13 |
| 10 | 9 | Props | Use rest props in runes mode | Pass through unknown props with $props rest destructuring | Spread a rest object onto the element | Use legacy $$restProps | let { class: className, ...rest } = $props(); <button {...rest}> | <button {...$$restProps}> | Low | https://svelte.dev/docs/svelte/$props | svelte 5 | active | 2026-08-13 |
| 11 | 10 | Bindings | Use bind: for two-way binding | Simplified input handling | bind:value for inputs | on:input with manual update | <input bind:value={name}> | <input value={name} on:input={e => name = e.target.value}> | Low | https://svelte.dev/docs/element-directives#bind-property | svelte 5 | active | 2026-08-13 |
| 12 | 11 | Bindings | Bind to DOM elements | Reference DOM nodes | bind:this for element reference | querySelector in onMount | <div bind:this={el}> | onMount(() => el = document.querySelector()) | Medium | svelte 5 | active | 2026-08-13 | |
| 13 | 12 | Bindings | Use bind:group for radios/checkboxes | Simplified group handling | bind:group for radio/checkbox groups | Manual checked handling | <input type="radio" bind:group={selected}> | <input type="radio" checked={selected === value}> | Low | svelte 5 | active | 2026-08-13 | |
| 14 | 13 | Events | Use on: for legacy event handlers | Event directive syntax for legacy components | on:click only while maintaining legacy mode | Introduce on: handlers in runes mode | <button on:click={handleClick}> | <button onclick={handleClick}> | Medium | https://svelte.dev/docs/svelte/legacy-on | svelte legacy <=4 | deprecated | 2026-08-13 |
| 15 | 14 | Events | Forward events with on:event in legacy mode | Legacy event forwarding without a handler | on:click only for a legacy component | Use legacy forwarding in runes mode | <button on:click> | <button onclick={onclick}> | Low | https://svelte.dev/docs/svelte/legacy-on | svelte legacy <=4 | deprecated | 2026-08-13 |
| 16 | 15 | Events | Use createEventDispatcher only in legacy components | Legacy custom component events | Retain dispatch while maintaining legacy components | Add createEventDispatcher to new runes-mode code | dispatch('save', { data }) | let { onsave } = $props() | Medium | https://svelte.dev/docs/svelte/svelte#createeventdispatcher | svelte legacy <=4 | deprecated | 2026-08-13 |
| 17 | 16 | Lifecycle | Use onMount for initialization | Run code after component mounts | onMount for setup and data fetching | Code in script body for side effects | onMount(() => fetchData()) | fetchData() in script body | High | https://svelte.dev/docs/svelte#onmount | svelte 5 | active | 2026-08-13 |
| 18 | 17 | Lifecycle | Return cleanup from onMount | Automatic cleanup on destroy | Return function from onMount | Separate onDestroy for paired cleanup | onMount(() => { sub(); return unsub }) | onMount(sub); onDestroy(unsub) | Medium | svelte 5 | active | 2026-08-13 | |
| 19 | 18 | Lifecycle | Use onDestroy sparingly | Only when onMount cleanup not possible | onDestroy for non-mount cleanup | onDestroy for mount-related cleanup | onDestroy for store unsubscribe | onDestroy(() => clearInterval(id)) | Low | svelte 5 | active | 2026-08-13 | |
| 20 | 19 | Lifecycle | Avoid beforeUpdate and afterUpdate | Legacy lifecycle hooks are unavailable in runes mode | Use $effect.pre and $effect only when synchronization is required | Use lifecycle hooks or reactive assignments for derived state | $effect.pre(() => measure()) | beforeUpdate(() => measure()) | Low | https://svelte.dev/docs/svelte/lifecycle-hooks | svelte 5 | active | 2026-08-13 |
| 21 | 20 | Stores | Use writable for mutable state | Basic reactive store | writable for shared mutable state | Local variables for shared state | const count = writable(0) | let count = 0 in module | Medium | https://svelte.dev/docs/svelte-store#writable | svelte 5 | active | 2026-08-13 |
| 22 | 21 | Stores | Use readable for read-only state | External data sources | readable for derived/external data | writable for read-only data | readable(0, set => interval(set)) | writable(0) for timer | Low | https://svelte.dev/docs/svelte-store#readable | svelte 5 | active | 2026-08-13 |
| 23 | 22 | Stores | Use derived for computed stores | Combine or transform stores | derived for computed values | Manual subscription for derived | derived(count, $c => $c * 2) | count.subscribe(c => doubled = c * 2) | Medium | https://svelte.dev/docs/svelte-store#derived | svelte 5 | active | 2026-08-13 |
| 24 | 23 | Stores | Use $ prefix for auto-subscription | Automatic subscribe/unsubscribe | $storeName in components | Manual subscription | {$count} | count.subscribe(c => value = c) | High | https://svelte.dev/docs/svelte/stores | svelte 5 | active | 2026-08-13 |
| 25 | 24 | Stores | Clean up custom subscriptions | Unsubscribe when component destroys | Return unsubscribe from onMount | Leave subscriptions open | onMount(() => store.subscribe(fn)) | store.subscribe(fn) in script | High | https://svelte.dev/docs/svelte/stores | svelte 5 | active | 2026-08-13 |
| 26 | 25 | Slots | Use slots for legacy composition | Legacy content projection with slot elements | Retain slots while maintaining legacy components | Introduce slot elements in runes-mode components | <slot>Default</slot> | {@render children()} | Medium | https://svelte.dev/docs/svelte/legacy-slots | svelte legacy <=4 | deprecated | 2026-08-13 |
| 27 | 26 | Slots | Use named slots for legacy areas | Legacy composition with multiple slot elements | Retain named slots only in legacy components | Add named slots to new runes-mode code | <slot name="header"> | {@render header()} | Low | https://svelte.dev/docs/svelte/legacy-slots | svelte legacy <=4 | deprecated | 2026-08-13 |
| 28 | 27 | Slots | Check slot content with $$slots in legacy mode | Legacy conditional slot rendering | Retain $$slots only in legacy components | Use $$slots in runes mode | {#if $$slots.footer}<slot name="footer"/>{/if} | {#if footer}{@render footer()}{/if} | Low | https://svelte.dev/docs/svelte/legacy-$$slots | svelte legacy <=4 | deprecated | 2026-08-13 |
| 29 | 28 | Styling | Use scoped styles by default | Styles scoped to component | <style> for component styles | Global styles for component | :global() only when needed | <style> all global | Medium | https://svelte.dev/docs/svelte-components#style | svelte 5 | active | 2026-08-13 |
| 30 | 29 | Styling | Use :global() sparingly | Escape scoping when needed | :global for third-party styling | Global for all styles | :global(.external-lib) | <style> without scoping | Medium | svelte 5 | active | 2026-08-13 | |
| 31 | 30 | Styling | Use CSS variables for theming | Dynamic styling | CSS custom properties | Inline styles for themes | style="--color: {color}" | style="color: {color}" | Low | svelte 5 | active | 2026-08-13 | |
| 32 | 31 | Transitions | Use built-in transitions | Svelte transition directives | transition:fade for simple effects | Manual CSS transitions | <div transition:fade> | <div class:fade={visible}> | Low | https://svelte.dev/docs/element-directives#transition-fn | svelte 5 | active | 2026-08-13 |
| 33 | 32 | Transitions | Use in: and out: separately | Different enter/exit animations | in:fly out:fade for asymmetric | Same transition for both | <div in:fly out:fade> | <div transition:fly> | Low | svelte 5 | active | 2026-08-13 | |
| 34 | 33 | Transitions | Add local modifier | Prevent ancestor trigger | transition:fade|local | Global transitions for lists | <div transition:slide|local> | <div transition:slide> | Medium | svelte 5 | active | 2026-08-13 | |
| 35 | 34 | Actions | Use actions for DOM behavior | Reusable DOM logic | use:action for DOM enhancements | onMount for each usage | <div use:clickOutside> | onMount(() => setupClickOutside(el)) | Medium | https://svelte.dev/docs/element-directives#use-action | svelte 5 | active | 2026-08-13 |
| 36 | 35 | Actions | Return update and destroy | Lifecycle methods for actions | Return { update, destroy } | Only initial setup | return { update(params) {}, destroy() {} } | return destroy only | Medium | svelte 5 | active | 2026-08-13 | |
| 37 | 36 | Actions | Pass parameters to actions | Configure action behavior | use:action={params} | Hardcoded action behavior | <div use:tooltip={options}> | <div use:tooltip> | Low | svelte 5 | active | 2026-08-13 | |
| 38 | 37 | Logic | Use {#if} for conditionals | Template conditionals | {#if} {:else if} {:else} | Ternary in expressions | {#if cond}...{:else}...{/if} | {cond ? a : b} for complex | Low | https://svelte.dev/docs/logic-blocks#if | svelte 5 | active | 2026-08-13 |
| 39 | 38 | Logic | Use {#each} for lists | List rendering | {#each} with key | Map in expression | {#each items as item (item.id)} | {items.map(i => `<div>${i}</div>`)} | Medium | svelte 5 | active | 2026-08-13 | |
| 40 | 39 | Logic | Always use keys in {#each} | Proper list reconciliation | (item.id) for unique key | Index as key or no key | {#each items as item (item.id)} | {#each items as item, i (i)} | High | https://svelte.dev/docs/svelte/each | svelte 5 | active | 2026-08-13 |
| 41 | 40 | Logic | Use {#await} for promises | Handle async states | {#await} for loading/error states | Manual promise handling | {#await promise}...{:then}...{:catch} | {#if loading}...{#if error} | Medium | https://svelte.dev/docs/logic-blocks#await | svelte 5 | active | 2026-08-13 |
| 42 | 41 | SvelteKit | Use +page.svelte for routes | File-based routing | +page.svelte for route components | Custom routing setup | routes/about/+page.svelte | routes/About.svelte | Medium | https://kit.svelte.dev/docs/routing | svelte 5 | active | 2026-08-13 |
| 43 | 42 | SvelteKit | Use +page.js for data loading | Load data before render | load function in +page.js | onMount for data fetching | export function load() {} | onMount(() => fetchData()) | High | https://kit.svelte.dev/docs/load | svelte 5 | active | 2026-08-13 |
| 44 | 43 | SvelteKit | Use +page.server.js for server-only | Server-side data loading | +page.server.js for sensitive data | +page.js for API keys | +page.server.js with DB access | +page.js with DB access | High | https://svelte.dev/docs/kit/load#universal-vs-server | svelte 5 | active | 2026-08-13 |
| 45 | 44 | SvelteKit | Use form actions | Server-side form handling | +page.server.js actions | API routes for forms | export const actions = { default } | fetch('/api/submit') | Medium | https://kit.svelte.dev/docs/form-actions | svelte 5 | active | 2026-08-13 |
| 46 | 45 | SvelteKit | Use $app/stores for legacy app state | Legacy SvelteKit page navigating and updated stores | Retain $app/stores only for legacy Svelte projects | Add $app/stores to new SvelteKit code | import { page } from '$app/stores' | import { page } from '$app/state' | Medium | https://svelte.dev/docs/kit/$app-stores | svelte legacy <=4 | deprecated | 2026-08-13 |
| 47 | 46 | Performance | Use {#key} for forced re-render | Reset component state | {#key id} for fresh instance | Manual destroy/create | {#key item.id}<Component/>{/key} | on:change={() => component = null} | Low | https://svelte.dev/docs/logic-blocks#key | svelte 5 | active | 2026-08-13 |
| 48 | 47 | Performance | Avoid unnecessary effects | Not every computation needs $effect | Use $derived for computed state and $effect only for external synchronization | Use effects for simple assignments | let doubled = $derived(count * 2) | $effect(() => doubled = count * 2) | Low | https://svelte.dev/docs/svelte/$effect | svelte 5 | active | 2026-08-13 |
| 49 | 48 | Performance | Avoid legacy immutable compiler assumptions | Runes use fine-grained reactivity without the legacy immutable option | Use current runes state and measure real bottlenecks | Add immutable mode to new runes components | $state for reactive data | <svelte:options immutable/> | Low | https://svelte.dev/docs/svelte/legacy-compiler-options | svelte 5 | active | 2026-08-13 |
| 50 | 49 | TypeScript | Use lang="ts" in script | TypeScript support | <script lang="ts"> | JavaScript for typed projects | <script lang="ts"> | <script> with JSDoc | Medium | https://svelte.dev/docs/typescript | svelte 5 | active | 2026-08-13 |
| 51 | 50 | TypeScript | Type props with an interface | Explicit prop types for $props | Destructure $props with an interface annotation | Use legacy $$Props or untyped props | interface Props { name: string }; let { name }: Props = $props() | interface $$Props { name: string } | Medium | https://svelte.dev/docs/svelte/typescript#typing-$props | svelte 5 | active | 2026-08-13 |
| 52 | 51 | TypeScript | Type legacy events with createEventDispatcher | Type-safe events in legacy components | Retain typed dispatch only for legacy components | Add dispatcher events to runes-mode components | createEventDispatcher<{ save: Data }>() | let { onsave }: Props = $props() | Medium | https://svelte.dev/docs/svelte/svelte#createeventdispatcher | svelte legacy <=4 | deprecated | 2026-08-13 |
| 53 | 52 | Accessibility | Use semantic elements | Proper HTML in templates | button nav main appropriately | div for everything | <button onclick={handleClick}> | <div onclick={handleClick}> | High | https://svelte.dev/docs/svelte/compiler-warnings#a11y_click_events_have_key_events | svelte 5 | active | 2026-08-13 |
| 54 | 53 | Accessibility | Add aria to dynamic content | Accessible state changes | aria-live for updates | Silent dynamic updates | <div aria-live="polite">{message}</div> | <div>{message}</div> | Medium | svelte 5 | active | 2026-08-13 | |
| 55 | 54 | Events | Use event properties in runes mode | Svelte 5 event handlers are component or element properties | Use onclick and callback props for new code | Use on: directives or createEventDispatcher in runes mode | <button onclick={handleClick}>Save</button> | <button on:click={handleClick}>Save</button> | High | https://svelte.dev/docs/svelte/v5-migration-guide#event-changes | svelte 5 | active | 2026-08-13 |
| 56 | 55 | SvelteKit | Use $app/state for current app state | SvelteKit exposes page navigating and updated as reactive state | Import current state from $app/state | Start new code with deprecated $app/stores | import { page } from '$app/state' | import { page } from '$app/stores' | High | https://svelte.dev/docs/kit/$app-state | svelte 5 | active | 2026-08-13 |