Skip to main content
LiveCodes supports Svelte 5 with the official Svelte compiler, including runes, reactive statements, and component composition.

Configuration

Language Name: svelte
File Extensions: .svelte, .app.svelte
Editor: Script editor
Compiler: Svelte 5 compiler
Runtime: Svelte 5.x with automatic imports

Features

Basic Component

<script>
  let count = $state(0);
  
  function increment() {
    count++;
  }
  
  function decrement() {
    count--;
  }
</script>

<div class="counter">
  <h2>Count: {count}</h2>
  <button on:click={increment}>Increment</button>
  <button on:click={decrement}>Decrement</button>
</div>

<style>
  .counter {
    padding: 2rem;
    text-align: center;
  }
  
  button {
    margin: 0.5rem;
    padding: 0.5rem 1rem;
    background: #ff3e00;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
  
  button:hover {
    background: #ff5722;
  }
</style>

Runes (Svelte 5)

Svelte 5 introduces runes for reactive state:
<script>
  // Reactive state
  let count = $state(0);
  
  // Derived state
  let doubled = $derived(count * 2);
  
  // Side effects
  $effect(() => {
    console.log(`Count is now ${count}`);
  });
  
  // Props
  let { title, initialCount = 0 } = $props();
</script>

<div>
  <h1>{title}</h1>
  <p>Count: {count}</p>
  <p>Doubled: {doubled}</p>
</div>

Reactive Declarations (Svelte 4 syntax)

Traditional reactive syntax still supported:
<script>
  let count = 0;
  
  // Reactive declaration
  $: doubled = count * 2;
  
  // Reactive statement
  $: {
    console.log(`Count: ${count}`);
    console.log(`Doubled: ${doubled}`);
  }
  
  // Reactive if
  $: if (count > 10) {
    console.log('Count is greater than 10');
  }
</script>

Component Props

Define and use props:
<script>
  // Svelte 5 runes
  let { title, count = 0, onClick } = $props();
  
  // Or Svelte 4 style
  export let title;
  export let count = 0;
  export let onClick = () => {};
</script>

<div>
  <h2>{title}</h2>
  <p>Count: {count}</p>
  <button on:click={onClick}>Click</button>
</div>

Events

Handle events and dispatch custom events:
<script>
  import { createEventDispatcher } from 'svelte';
  
  const dispatch = createEventDispatcher();
  
  function handleClick() {
    dispatch('submit', {
      timestamp: Date.now(),
    });
  }
  
  function handleInput(event) {
    console.log(event.target.value);
  }
</script>

<div>
  <input on:input={handleInput} />
  <button on:click={handleClick}>Submit</button>
</div>

Bindings

Two-way data binding:
<script>
  let name = $state('');
  let checked = $state(false);
  let selected = $state('');
  let value = $state(50);
</script>

<div>
  <!-- Text input -->
  <input bind:value={name} />
  <p>Hello, {name}!</p>
  
  <!-- Checkbox -->
  <input type="checkbox" bind:checked />
  <p>Checked: {checked}</p>
  
  <!-- Select -->
  <select bind:value={selected}>
    <option value="a">Option A</option>
    <option value="b">Option B</option>
  </select>
  
  <!-- Range -->
  <input type="range" bind:value min="0" max="100" />
  <p>Value: {value}</p>
</div>

Conditionals

Conditional rendering:
<script>
  let user = $state({ loggedIn: false });
  let count = $state(0);
</script>

{#if user.loggedIn}
  <p>Welcome back!</p>
{:else if user.pending}
  <p>Loading...</p>
{:else}
  <p>Please log in</p>
{/if}

{#if count > 10}
  <p>Count is greater than 10</p>
{/if}

Loops

List rendering with {#each}:
<script>
  let items = $state([
    { id: 1, name: 'Apple' },
    { id: 2, name: 'Banana' },
    { id: 3, name: 'Cherry' },
  ]);
</script>

<ul>
  {#each items as item (item.id)}
    <li>{item.name}</li>
  {:else}
    <li>No items</li>
  {/each}
</ul>

<!-- With index -->
{#each items as item, index (item.id)}
  <p>{index + 1}. {item.name}</p>
{/each}

Await Blocks

Handle async data:
<script>
  async function fetchUsers() {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    return response.json();
  }
  
  let usersPromise = $state(fetchUsers());
</script>

{#await usersPromise}
  <p>Loading...</p>
{:then users}
  <ul>
    {#each users as user}
      <li>{user.name}</li>
    {/each}
  </ul>
{:catch error}
  <p>Error: {error.message}</p>
{/await}

Component Features

Slots

Component composition:
<!-- Card.svelte -->
<div class="card">
  <div class="card-header">
    <slot name="header">Default Header</slot>
  </div>
  
  <div class="card-body">
    <slot>Default content</slot>
  </div>
  
  <div class="card-footer">
    <slot name="footer" />
  </div>
</div>

<!-- Usage -->
<script>
  import Card from './Card.svelte';
</script>

<Card>
  <h2 slot="header">Custom Header</h2>
  <p>Card content goes here</p>
  <button slot="footer">Action</button>
</Card>

Context API

Share data between components:
<!-- Parent.svelte -->
<script>
  import { setContext } from 'svelte';
  
  setContext('theme', {
    color: 'dark',
    toggle: () => {
      // toggle logic
    },
  });
</script>

<!-- Child.svelte -->
<script>
  import { getContext } from 'svelte';
  
  const theme = getContext('theme');
</script>

<button on:click={theme.toggle}>
  Current theme: {theme.color}
</button>

Lifecycle

Component lifecycle hooks:
<script>
  import { onMount, onDestroy, beforeUpdate, afterUpdate } from 'svelte';
  
  onMount(() => {
    console.log('Component mounted');
    return () => {
      console.log('Cleanup on unmount');
    };
  });
  
  onDestroy(() => {
    console.log('Component destroyed');
  });
  
  beforeUpdate(() => {
    console.log('Before update');
  });
  
  afterUpdate(() => {
    console.log('After update');
  });
</script>

Stores

Reactive stores for shared state:
<script>
  import { writable, derived, readable } from 'svelte/store';
  
  // Writable store
  const count = writable(0);
  
  // Derived store
  const doubled = derived(count, $count => $count * 2);
  
  // Readable store
  const time = readable(new Date(), (set) => {
    const interval = setInterval(() => {
      set(new Date());
    }, 1000);
    
    return () => clearInterval(interval);
  });
  
  // Auto-subscription with $
  $: console.log('Count:', $count);
</script>

<div>
  <p>Count: {$count}</p>
  <p>Doubled: {$doubled}</p>
  <p>Time: {$time.toLocaleTimeString()}</p>
  
  <button on:click={() => count.update(n => n + 1)}>
    Increment
  </button>
</div>

Animations & Transitions

Transitions

<script>
  import { fade, fly, slide, scale } from 'svelte/transition';
  import { quintOut } from 'svelte/easing';
  
  let visible = $state(true);
</script>

{#if visible}
  <div transition:fade>Fades in and out</div>
{/if}

{#if visible}
  <div transition:fly={{ y: 200, duration: 500 }}>
    Flies in and out
  </div>
{/if}

{#if visible}
  <div transition:scale={{ start: 0, duration: 500, easing: quintOut }}>
    Scales in and out
  </div>
{/if}

<button on:click={() => visible = !visible}>
  Toggle
</button>

Animations

<script>
  import { flip } from 'svelte/animate';
  import { quintOut } from 'svelte/easing';
  
  let items = $state([1, 2, 3, 4, 5]);
  
  function shuffle() {
    items = items.sort(() => Math.random() - 0.5);
  }
</script>

<button on:click={shuffle}>Shuffle</button>

{#each items as item (item)}
  <div animate:flip={{ duration: 500, easing: quintOut }}>
    {item}
  </div>
{/each}

Styling

Scoped Styles

Styles are scoped by default:
<div class="card">
  <h2>Title</h2>
</div>

<style>
  /* Only applies to this component */
  .card {
    border: 1px solid #ccc;
    padding: 1rem;
  }
  
  h2 {
    color: #333;
  }
</style>

Global Styles

<style>
  :global(body) {
    margin: 0;
    font-family: sans-serif;
  }
  
  /* Scoped to component, global class name */
  :global(.button) {
    padding: 0.5rem 1rem;
  }
</style>

Dynamic Styles

<script>
  let color = $state('blue');
  let size = $state(16);
</script>

<p style="color: {color}; font-size: {size}px">
  Styled text
</p>

<p style:color style:font-size="{size}px">
  Alternative syntax
</p>

Example Projects

Todo App

<script>
  let todos = $state([]);
  let newTodo = $state('');
  
  function addTodo() {
    if (newTodo.trim()) {
      todos = [...todos, {
        id: Date.now(),
        text: newTodo,
        done: false,
      }];
      newTodo = '';
    }
  }
  
  function toggleTodo(id) {
    todos = todos.map(todo =>
      todo.id === id ? { ...todo, done: !todo.done } : todo
    );
  }
  
  function deleteTodo(id) {
    todos = todos.filter(todo => todo.id !== id);
  }
</script>

<div class="todo-app">
  <h1>Todo List</h1>
  
  <input
    bind:value={newTodo}
    on:keypress={e => e.key === 'Enter' && addTodo()}
    placeholder="Add a todo..."
  />
  <button on:click={addTodo}>Add</button>
  
  <ul>
    {#each todos as todo (todo.id)}
      <li class:done={todo.done}>
        <input
          type="checkbox"
          checked={todo.done}
          on:change={() => toggleTodo(todo.id)}
        />
        {todo.text}
        <button on:click={() => deleteTodo(todo.id)}>Delete</button>
      </li>
    {/each}
  </ul>
</div>

<style>
  .done {
    text-decoration: line-through;
    opacity: 0.6;
  }
</style>

Code Formatting

Automatic formatting with Prettier:
<!-- Before formatting -->
<script>let count=0;</script>
<button on:click={()=>count++}>Click</button>

<!-- After formatting (Ctrl+Shift+F) -->
<script>
  let count = 0;
</script>

<button on:click={() => count++}>Click</button>

Best Practices

Prefer runes for clearer reactive state:
<script>
// Good (Svelte 5)
let count = $state(0);
let doubled = $derived(count * 2);

// Old (Svelte 4)
let count = 0;
$: doubled = count * 2;
</script>
Always provide keys for list items:
<!-- Good -->
{#each items as item (item.id)}
  <div>{item.name}</div>
{/each}

<!-- Bad - no key -->
{#each items as item}
  <div>{item.name}</div>
{/each}
Keep reactive declarations simple:
<script>
// Good
$: doubled = count * 2;

// Avoid complex logic
$: {
  // Multiple lines of complex calculations
  // Better to use a function
}
</script>

Vue

Alternative component framework

React

JSX-based framework

SCSS

Use SCSS in style blocks

TypeScript

Add type safety to Svelte