11 KiB
11 KiB
| 1 | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL |
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 1 | Reactivity | Use $: for reactive statements | Automatic dependency tracking | $: for derived values | Manual recalculation | $: doubled = count * 2 | let doubled; count && (doubled = count * 2) | Medium | https://svelte.dev/docs/svelte-components#script-3-$-marks-a-statement-as-reactive |
| 3 | 2 | Reactivity | Trigger reactivity with assignment | Svelte tracks assignments not mutations | Reassign arrays/objects to trigger update | Mutate without reassignment | items = [...items, newItem] | items.push(newItem) | High | https://svelte.dev/docs/svelte-components#script-2-assignments-are-reactive |
| 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 |
| 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 | |
| 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 | |
| 7 | 6 | Props | Export let for props | Declare props with export let | export let propName | Props without export | export let count = 0 | let count = 0 | High | https://svelte.dev/docs/svelte-components#script-1-export-creates-a-component-prop |
| 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 | |
| 9 | 8 | Props | Provide default values | Default props with assignment | export let count = 0 | Required props without defaults | export let count = 0 | export let count | Low | |
| 10 | 9 | Props | Use spread props | Pass through unknown props | {...$$restProps} on elements | Manual prop forwarding | <button {...$$restProps}> | <button class={$$props.class}> | Low | https://svelte.dev/docs/basic-markup#attributes-and-props |
| 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 |
| 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 | |
| 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 | |
| 14 | 13 | Events | Use on: for event handlers | Event directive syntax | on:click={handler} | addEventListener in onMount | <button on:click={handleClick}> | onMount(() => btn.addEventListener()) | Medium | https://svelte.dev/docs/element-directives#on-eventname |
| 15 | 14 | Events | Forward events with on:event | Pass events to parent | on:click without handler | createEventDispatcher for DOM events | <button on:click> | dispatch('click', event) | Low | |
| 16 | 15 | Events | Use createEventDispatcher | Custom component events | dispatch for custom events | on:event for custom events | dispatch('save', { data }) | on:save without dispatch | Medium | https://svelte.dev/docs/svelte#createeventdispatcher |
| 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 |
| 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 | |
| 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 | |
| 20 | 19 | Lifecycle | Avoid beforeUpdate/afterUpdate | Usually not needed | Reactive statements instead | beforeUpdate for derived state | $: if (x) doSomething() | beforeUpdate(() => doSomething()) | Low | |
| 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 |
| 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 |
| 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 |
| 24 | 23 | Stores | Use $ prefix for auto-subscription | Automatic subscribe/unsubscribe | $storeName in components | Manual subscription | {$count} | count.subscribe(c => value = c) | High | |
| 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 | |
| 26 | 25 | Slots | Use slots for composition | Content projection | <slot> for flexible content | Props for all content | <slot>Default</slot> | <Component content="text"/> | Medium | https://svelte.dev/docs/special-elements#slot |
| 27 | 26 | Slots | Name slots for multiple areas | Multiple content areas | <slot name="header"> | Single slot for complex layouts | <slot name="header"><slot name="footer"> | <slot> with complex conditionals | Low | |
| 28 | 27 | Slots | Check slot content with $$slots | Conditional slot rendering | $$slots.name for conditional rendering | Always render slot wrapper | {#if $$slots.footer}<slot name="footer"/>{/if} | <div><slot name="footer"/></div> | Low | |
| 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 |
| 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 | |
| 31 | 30 | Styling | Use CSS variables for theming | Dynamic styling | CSS custom properties | Inline styles for themes | style="--color: {color}" | style="color: {color}" | Low | |
| 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 |
| 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 | |
| 34 | 33 | Transitions | Add local modifier | Prevent ancestor trigger | transition:fade|local | Global transitions for lists | <div transition:slide|local> | <div transition:slide> | Medium | |
| 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 |
| 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 | |
| 37 | 36 | Actions | Pass parameters to actions | Configure action behavior | use:action={params} | Hardcoded action behavior | <div use:tooltip={options}> | <div use:tooltip> | Low | |
| 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 |
| 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 | |
| 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 | |
| 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 |
| 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 |
| 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 |
| 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 | |
| 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 |
| 46 | 45 | SvelteKit | Use $app/stores for app state | $page $navigating $updated | $page for current page data | Manual URL parsing | import { page } from '$app/stores' | window.location.pathname | Medium | https://kit.svelte.dev/docs/modules#$app-stores |
| 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 |
| 48 | 47 | Performance | Avoid unnecessary reactivity | Not everything needs $: | $: only for side effects | $: for simple assignments | $: if (x) console.log(x) | $: y = x (when y = x works) | Low | |
| 49 | 48 | Performance | Use immutable compiler option | Skip equality checks | immutable: true for large lists | Default for all components | <svelte:options immutable/> | Default without immutable | Low | |
| 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 |
| 51 | 50 | TypeScript | Type props with interface | Explicit prop types | interface $$Props for types | Untyped props | interface $$Props { name: string } | export let name | Medium | |
| 52 | 51 | TypeScript | Type events with createEventDispatcher | Type-safe events | createEventDispatcher<Events>() | Untyped dispatch | createEventDispatcher<{ save: Data }>() | createEventDispatcher() | Medium | |
| 53 | 52 | Accessibility | Use semantic elements | Proper HTML in templates | button nav main appropriately | div for everything | <button on:click> | <div on:click> | High | |
| 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 |