Skip to content
Advanced Svelte

Advanced Svelte

Advanced Reactivity

  • Raw State

Characteristics: Changes to properties and content will not trigger updates

1
let data = $state.raw(poll());
  • Reactive Classes
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Box {
  width = $state(0);
  height = $state(0);
  area = $derived(this.width * this.height);

  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  embiggen(amount) {
    this.width += amount;
    this.height += amount;
  }
}

class Box {
  #width = $state(0);
  #height = $state(0);
  area = $derived(this.#width * this.#height);

  constructor(width, height) {
    this.#width = width;
    this.#height = height;
  }

  get width() {
    return this.#width;
  }

  get height() {
    return this.#height;
  }

  set width(value) {
    this.#width = Math.max(0, Math.min(MAX_SIZE, value));
  }

  set height(value) {
    this.#height = Math.max(0, Math.min(MAX_SIZE, value));
  }

  embiggen(amount) {
    this.width += amount;
    this.height += amount;
  }
}
  • Built-in Reactive Classes

Supports Map, Set, Date, URL, URLSearchParams

1
2
3
import { SvelteDate } from "svelte/reactivity";

let date = new SvelteDate();
  • store

Content Reuse

  • #snippet

snippets can also be passed to child components as props

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<table>
  <tbody>
    {#snippet monkey(emoji, description)}
      <tr>
        <td>{emoji}</td>
        <td>{description}</td>
        <td>\\u{emoji.charCodeAt(0).toString(16)}\\u{emoji.charCodeAt(1).toString(16)}</td>
        <td>&amp#{emoji.codePointAt(0)}</td>
      </tr>
    {/snippet}

    {@render monkey('🙈', 'see no evil')}
    {@render monkey('🙉', 'hear no evil')}
    {@render monkey('🙊', 'speak no evil')}
  </tbody>
</table>
  • Passing snippets as component props
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<FilteredList
  data={colors}
  field="name"
  {header}
  {row}
></FilteredList>

{#snippet header()}
<!-- ... -->
{/snippet}

{#snippet row()}
<!-- ... -->
{/snippet}

<!-- Syntactic sugar: snippets declared inside a component automatically become props for those components -->
<FilteredList
  data={colors}
  field="name"
>
  {#snippet header()}
  <!-- ... -->
  {/snippet}

  {#snippet row()}
  <!-- ... -->
  {/snippet}
</FilteredList>

Motion

  • Tween
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<script>
  import { Tween } from 'svelte/motion';
  import { cubicOut } from 'svelte/easing';

  let progress = new Tween(0, {
    duration: 400,
    easing: cubicOut
  });
</script>

<progress value={progress.current}></progress>

<button onclick={() => (progress.target = 0)}>
  0%
</button>

<button onclick={() => (progress.target = 1)}>
  100%
</button>
  • Spring
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script>
  import { Spring } from 'svelte/motion';

  let coords = new Spring({ x: 50, y: 50 }, {
    stiffness: 0.1,
    damping: 0.25
  });

  let size = new Spring(10);
</script>

<svg
  onmousemove={(e) => {
    coords.target = { x: e.clientX, y: e.clientY };
  }}
  onmousedown={() => (size.target = 30)}
  onmouseup={() => (size.target = 10)}
  role="presentation"
>
  <circle
    cx={coords.current.x}
    cy={coords.current.y}
    r={size.current}
  />
</svg>

Advanced (Two-way) Binding

  • contenteditable

Supports binding textContent and innerHTML

1
<div bind:innerHTML={html} contenteditable></div>
  • each blocks
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{#each todos as todo}
  <li class={{ done: todo.done }}>
    <input
      type="checkbox"
      bind:checked={todo.done}
    />

    <input
      type="text"
      placeholder="What needs to be done?"
      bind:value={todo.text}
    />
  </li>
{/each}
  • Media elements
1
2
3
4
5
6
<audio
  {src}
  bind:currentTime={time}
  bind:duration
  bind:paused
></audio>
  • Dimensions

Supports clientWidth, clientHeight, offsetWidth, offsetHeight

Read-only bindings

1
2
<div bind:clientWidth={w} bind:clientHeight={h}>
</div>
  • DOM Elements

Read-only bindings

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<script>
  let canvas;

  $effect(() => {
    const context = canvas.getContext('2d');
    // ...
  });
</script>

<canvas bind:this={canvas} width={32} height={32}></canvas>
  • Making component props bindable
1
let { value = $bindable(""), onsubmit } = $props();
  • Component Instances
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- Child Component -->
<script>
  export function f() {}
</script>

<!-- Parent Component -->
<script>
  let child;
</script>

<Child bind:this={child} />
<button onclick={child.f}>Button</button>

Advanced Transitions

  • Deferred Transitions
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import { crossfade } from "svelte/transition";
import { quintOut } from "svelte/easing";

export const [send, receive] = crossfade({
  duration: (d) => Math.sqrt(d * 200),

  fallback(node, params) {
    const style = getComputedStyle(node);
    const transform = style.transform === "none" ? "" : style.transform;

    return {
      duration: 600,
      easing: quintOut,
      css: (t) => `
        transform: ${transform} scale(${t});
        opacity: ${t}
      `,
    };
  },
});
1
2
3
4
<li
  in:receive={{ key: todo.id }}
  out:send={{ key: todo.id }}
/>
  • Animations (animate:)

Provides animation effects for elements that are not transitioning

1
2
3
4
5
6
<li
  class={{ done: todo.done }}
  in:receive={{ key: todo.id }}
  out:send={{ key: todo.id }}
  animate:flip
>

Context

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!-- Set -->
<script>
  import { setContext } from 'svelte';

  setContext('key', value);
</script>

<!-- Get -->
<script>
  import { getContext } from 'svelte';

  const value = getContext('key');
</script>

Special Elements

  • <svelte:window>

    • Can add event listeners
    • Can bind innerWidth, innerHeight, outerWidth, outerHeight, scrollX, scrollY, online (window.navigator.onLine). All are read-only except scrollX and scrollY.
  • <svelte:document>

    • Can add event listeners
  • <svelte:body>

    • Can add event listeners
  • <svelte:head>

    • Allows adding content to the HTML <head>
    • In SSR mode, it will be returned separately from other HTML content
  • <svelte:element>

    • Can specify the type of the element via the this property
1
2
3
4
5
6
7
8
<script>
  const options = ['h1', 'h2', 'h3', 'p', 'marquee'];
  let selected = $state(options[0]);
</script>

<svelte:element this={selected}>
  I'm a <code>&lt;{selected}&gt;</code> element
</svelte:element>
  • <svelte:boundary>
    • Used to handle component loading errors
1
2
3
4
5
6
7
8
<svelte:boundary onerror={(e) => console.error(e)}>
  <FlakyComponent />

  {#snippet failed(error, reset)}
    <p>Oops! {error.message}</p>
    <button onclick={reset}>Reset</button>
  {/snippet}
</svelte:boundary>

<script module>

Decouples code from component instances

  • Code will only run when the module is first evaluated
  • Can use export to export (but cannot use default export, as the default export is the component itself)
Last updated on