Files
Job-Info/svelte-reference/00-QUICK_REFERENCE.md
T
aewing 0b76a4b119 Cleanup: Remove old modes, add Svelte 5 reference library, update orchestrator
- Delete Mode1Test, Mode2Test, Mode3Test, Mode5Test (keep only Mode6Test)
- Delete old documentation, Docker files, and k8s configs
- Add comprehensive Svelte 5 reference library (svelte-reference/)
- Update ModeSwitch orchestrator:
  - Fix port to 3005 (never use 3000/3001)
  - Add API endpoints for mode switching
  - Add bun-types and proper tsconfig
  - Clean up mode folder mappings
2026-01-21 03:56:01 +00:00

9.0 KiB

Svelte 5 Quick Reference Card

Runes Cheat Sheet

<script>
  // ═══════════════════════════════════════════════════════════
  // $state - Reactive state declaration
  // ═══════════════════════════════════════════════════════════
  let count = $state(0);                    // Primitive
  let user = $state({ name: '', age: 0 });  // Object (deep reactive)
  let items = $state<string[]>([]);         // Array (deep reactive)
  
  // Direct mutations work with $state!
  items.push('item');      // ✅ Reactive
  user.name = 'John';      // ✅ Reactive
  count++;                 // ✅ Reactive
  
  // ═══════════════════════════════════════════════════════════
  // $derived - Computed values (auto-tracks dependencies)
  // ═══════════════════════════════════════════════════════════
  let doubled = $derived(count * 2);        // Simple expression
  
  let filtered = $derived.by(() => {        // Complex computation
    return items.filter(i => i.includes(filter));
  });
  
  // ═══════════════════════════════════════════════════════════
  // $effect - Side effects (auto-tracks, auto-cleanup)
  // ═══════════════════════════════════════════════════════════
  $effect(() => {
    console.log('Count is:', count);        // Runs when count changes
    
    return () => {                          // Optional cleanup
      console.log('Cleaning up');
    };
  });
  
  $effect.pre(() => {
    // Runs BEFORE DOM updates
  });
  
  // ═══════════════════════════════════════════════════════════
  // $props - Component props
  // ═══════════════════════════════════════════════════════════
  let { 
    name,                    // Required prop
    count = 0,               // With default
    children,                // Snippet (replaces default slot)
    header,                  // Named snippet
    onclick,                 // Event callback
    ...rest                  // Rest props
  } = $props();
  
  // ═══════════════════════════════════════════════════════════
  // $bindable - Two-way bindable props
  // ═══════════════════════════════════════════════════════════
  let { value = $bindable('') } = $props();
  // Parent: <Input bind:value />
  
  // ═══════════════════════════════════════════════════════════
  // $inspect - Debug reactive values (dev only)
  // ═══════════════════════════════════════════════════════════
  $inspect(count);                          // Logs on change
  $inspect(count).with(console.trace);      // Custom logger
</script>

Template Syntax

<!-- Rendering Snippets (replaces slots) -->
{@render children?.()}
{@render header?.()}
{@render row({ item, index })}

<!-- Conditionals -->
{#if condition}
  ...
{:else if other}
  ...
{:else}
  ...
{/if}

<!-- Loops -->
{#each items as item, index (item.id)}
  ...
{/each}

<!-- Await -->
{#await promise}
  <Loading />
{:then data}
  <Result {data} />
{:catch error}
  <Error {error} />
{/await}

<!-- Key block (force re-render) -->
{#key value}
  <Component />
{/key}

<!-- HTML (dangerous) -->
{@html content}

<!-- Debug -->
{@debug variable}

<!-- Define snippets -->
{#snippet name(param1, param2)}
  <div>{param1}</div>
{/snippet}

Component Patterns

<!-- Basic Component -->
<script lang="ts">
  import type { Snippet } from 'svelte';
  
  interface Props {
    title: string;
    variant?: 'primary' | 'secondary';
    disabled?: boolean;
    onclick?: () => void;
    children: Snippet;
  }
  
  let {
    title,
    variant = 'primary',
    disabled = false,
    onclick,
    children
  }: Props = $props();
</script>

<button 
  class="btn btn-{variant}"
  {disabled}
  {onclick}
>
  {@render children()}
</button>

<!-- With Named Snippets -->
<script lang="ts">
  import type { Snippet } from 'svelte';
  
  interface Props {
    header?: Snippet;
    children?: Snippet;
    footer?: Snippet<[{ close: () => void }]>;
  }
  
  let { header, children, footer }: Props = $props();
  
  function close() {
    // close logic
  }
</script>

{@render header?.()}
{@render children?.()}
{@render footer?.({ close })}

State Management

// stores/counter.svelte.ts
export class CounterStore {
  count = $state(0);
  doubled = $derived(this.count * 2);
  
  increment = () => { this.count++; }
  decrement = () => { this.count--; }
  reset = () => { this.count = 0; }
}

export const counter = new CounterStore();

// Usage in component
import { counter } from '$lib/stores/counter.svelte';

// Read: counter.count, counter.doubled
// Write: counter.increment()

SvelteKit Essentials

// +page.ts (Universal load)
export async function load({ params, fetch }) {
  const data = await fetch(`/api/items/${params.id}`);
  return { item: await data.json() };
}

// +page.server.ts (Server-only load)
export async function load({ params, locals }) {
  return { user: locals.user };
}

// Form Actions
export const actions = {
  default: async ({ request }) => {
    const data = await request.formData();
    // Process form
    return { success: true };
  },
  named: async ({ request }) => {
    // Named action: <form action="?/named">
  }
};

// API Routes (+server.ts)
export async function GET({ params }) {
  return json({ data: 'value' });
}

export async function POST({ request }) {
  const body = await request.json();
  return json({ created: true }, { status: 201 });
}

File Structure

src/
├── app.html              # HTML template
├── app.d.ts              # Type declarations
├── lib/
│   ├── components/       # Reusable components
│   ├── stores/           # State (.svelte.ts files)
│   ├── utils/            # Utility functions
│   ├── hooks/            # Custom hooks
│   └── server/           # Server-only code
├── routes/
│   ├── +page.svelte      # Page component
│   ├── +page.ts          # Page load (universal)
│   ├── +page.server.ts   # Page load (server) + actions
│   ├── +layout.svelte    # Layout component
│   ├── +layout.ts        # Layout load
│   ├── +error.svelte     # Error page
│   └── api/
│       └── +server.ts    # API endpoint
└── hooks.server.ts       # Server hooks

Common Anti-Patterns

<!-- ❌ DON'T: Destructure reactive state -->
let { name } = user;  // Loses reactivity!

<!-- ✅ DO: Access properties directly -->
{user.name}

<!-- ❌ DON'T: Use $effect for derived values -->
$effect(() => {
  doubled = count * 2;  // Use $derived instead!
});

<!-- ✅ DO: Use $derived -->
let doubled = $derived(count * 2);

<!-- ❌ DON'T: Forget optional chaining for snippets -->
{@render children()}  // May error if undefined

<!-- ✅ DO: Use optional chaining -->
{@render children?.()}

<!-- ❌ DON'T: Modify state in $derived -->
let bad = $derived(count++);  // Side effect!

<!-- ✅ DO: Keep $derived pure -->
let good = $derived(count + 1);

TypeScript Quick Types

import type { 
  Snippet,           // Snippet type
  Component,         // Component type
  ComponentProps     // Extract props from component
} from 'svelte';

import type {
  PageData,          // From +page.ts load
  PageServerData,    // From +page.server.ts load
  Actions,           // Form actions type
  RequestHandler     // API route handler
} from './$types';

// Snippet with params
type ItemSnippet = Snippet<[{ item: Item; index: number }]>;

// Component props
type ButtonProps = ComponentProps<typeof Button>;

Key Commands

# Create new project
npx sv create my-app

# Development
npm run dev

# Build
npm run build

# Preview production build
npm run preview

# Type checking
npm run check

# Run migration
npx sv migrate svelte-5