Hi everyone, How are you?

Some time in the early noughties...

...I learned about the wonders of web development.

CSS-based layouts were slowly overtaking HTML table layouts.

JavaScript was a no-no.

At that time, I fell in love with building

for the web.

Just semantic HTML, a little CSS, progressively enhanced with JavaScript.

Pushed to any available web host.

Done. Simple and Beautiful.

But things changed...

Expectations towards web things grew.

Complexity rose.

Beauty and simplicity went away.

But a little later, something happened...

My personal introduction to Svelte in 2019...

My personal introduction to Svelte in 2019...

...brought back some of the joy.

I started using Svelte that day and never

looked back!

News from the Land of Svelte

Nils Röhrig | Loql

  1. Core characteristics of Svelte

  2. Runes & Reactivity

  3. Event Listeners

  4. Snippets

  5. My Opinion on the Changes

  1. Core characteristics of Svelte

  2. Runes & Reactivity

  3. Event Listeners

  4. Snippets

  5. Quality of Life Features

  6. My Opinion on the Changes

What is Svelte?

Svelte is a UI framework that uses a compiler to let you write breathtakingly concise components that do minimal work in the browser, using languages you already know — HTML, CSS and JavaScript. It’s a love letter to web development.

- Svelte Website

  1. Svelte is a UI component framework.
  2. Svelte has a compiler.
  3. Svelte is “a love letter to web development”.

Svelte as a Framework

Logo
Register
Home
E-Mail
Submit

etc. pp...

Component

Component

Component

Svelte as a Framework

Svelte as a Compiler

<h1>Simple Component</h1>

Component.svelte

Svelte as a Compiler

Svelte as a Compiler

<h1>Simple Component</h1>

Svelte as a Compiler

<h1>Simple Component</h1>

Svelte as a Compiler

import { SvelteComponent, detach, element, init,
         insert, noop, safe_not_equal } from "svelte/internal";

function create_fragment(ctx) {
  let h1;

  return {
    c() {
      h1 = element("h1");
      h1.textContent = "Simple Component";
    },
    m(target, anchor) {
      insert(target, h1, anchor);
    },
    p: noop,
    i: noop,
    o: noop,
    d(detaching) {
      if (detaching) detach(h1);
    },
  };
}

class SimpleComponent extends SvelteComponent {
  constructor(options) {
    super();
    init(this, options, null, create_fragment, safe_not_equal, {});
  }
}

export default SimpleComponent;

Svelte as a Compiler

import * as $ from "svelte/internal/client";

var root = $.template(`<h1>Simple Component</h1>`);

export default function Component($$anchor) {
  var h1 = root();
  $.append($$anchor, h1);
}

Component.js

Svelte as a Compiler

<h1>Simple Component</h1>
import { mount } from "svelte";
import Component from "./Component.js";

mount(Component, {
  target: document.querySelector("#root")
});

main.js

Svelte as a Love Letter

Svelte as a Love Letter

<!-- DECLARATIVE MARKUP -->
<h1>Hello Workshop People!</h1>
<p>{"Any expression works here!"}</p>

Component.svelte

<h1>Simple Component</h1>
<script>
  // STATE & BEHAVIOR
</script>

<!-- DECLARATIVE MARKUP -->
<h1>Hello Workshop People!</h1>
<p>{"Any expression works here!"}</p>

<style>
  /* PRESENTATION */
</style>

Component.svelte

Svelte as a Love Letter

Logic Blocks

& Directives

in Svelte

Logic Blocks

in Svelte

& Directives

& Directives

{#if condition}
	<p>The condition is true!</p>
{:else}
	<p>The condition is false!</p>
{/if}

{#each list as item}
	<p>Item: {item}</p>
{/each}

Logic Blocks

in Svelte

Directives

in Svelte

& Directives

& Directives

<script>
	let name = $state("world");
</script>

<h1>Hello {name}!</h1>
Enter your name: <input bind:value={name} />

Directives

in Svelte

Runes &

Reactivity in Svelte

Reactivity in Svelte

<!-- Svelte 4 -->
<script>
	let counter = 0;

	const increment = () => counter++;
	const decrement = () => counter--;
</script>

<button on:click={decrement}>-</button>
<output>{counter}</output>
<button on:click={increment}>+</button>

4

Reactivity in Svelte 4

<!--Svelte 4 | App.svelte -->
<script>
	import { counter } from './store.js'
</script>

<button on:click={() => counter.dec()}>-</button>
<output>{$counter}</output>
<button on:click={() => counter.inc()}>+</button>
// Svelte 4 | stores.js
import {writable} from 'svelte/store';

const {update, subscribe} = writable(0);

export const counter = {
	subscribe,
	inc: () => update(currentValue => currentValue + 1),
	dec: () => update(currentValue => currentValue - 1)
};

& Directives

Runes in Svelte

Runes are symbols that you use in .svelte and .svelte.js / .svelte.ts files to control the Svelte compiler. If you think of Svelte as a language, runes are part of the syntax — they are keywords.

- Svelte Documentation

  1. Runes are keywords and part of Svelte syntax
  2. Specific instructions to Svelte compiler
  3. Usable in- and outside of Svelte components
  • The $state rune
  • The $derived rune
  • The $effect rune
  • The $props rune

Runes in Svelte

The $state Rune

The $state Rune

<!-- Svelte 5 -->
<script>
	let name = $state("world");
</script>

<h1>Hello {name}!</h1>
Enter your name: <input bind:value={name} />

The $state Rune

<!-- Svelte 4 -->
<script>
	let name = "world";
</script>

<h1>Hello {name}!</h1>
Enter your name: <input bind:value={name} />

The $state Rune

<!-- Svelte 5 -->
<script>
	let name = $state("world");
	let anotherName = "reactivity";
</script>

<!-- This text will change when input is changed -->
<h1>Hello {name}!</h1>
Enter your name: <input bind:value={name} />

<!-- This text won't change when input is changed -->
<h1>Hello {anotherName}!</h1>
Enter another name: <input bind:value={anotherName} />

The $derived Rune

The $derived Rune

<!-- Svelte 5 -->
<script>
	let price = $state(1);
	let quantity = $state(0);

	let total = $derived(price * quantity);
</script>


Quantity: <input bind:value={quantity} type="number" />
Price: <input bind:value={price} type="number" />
Total: <output>{total}</output>

The $derived Rune

<!-- Svelte 4 -->
<script>
	let price = 1;
	let quantity = 0;

	$: total = price * quantity;
</script>


Quantity: <input bind:value={quantity} type="number" />
Price: <input bind:value={price} type="number" />
Total: <output>{total}</output>

The $effect Rune

The $effect Rune

<!-- Svelte 5 -->
<script>
	let quantity = $state(0);

	$effect(() => {    	
        console.log(`You've entered ${quantity}.`);
	});
</script>

Quantity: <input bind:value={quantity} type="number" />

The $effect Rune

<!-- Svelte 4 -->
<script>
	let quantity = 0;

	$: console.log(`You've entered ${quantity}.`);
</script>

Quantity: <input bind:value={quantity} type="number" />

Avoid overusing $effect! When you do too much work in effects, code often becomes difficult to understand and maintain.

- Svelte Documentation

The $props Rune

The $props Rune

<!-- Svelte 5 | App.svelte -->
<script>
	import Card from './Card.svelte';
</script>

<Card 
	title="Card Title" 
    body="Card Body." 
    footer="Card Footer."
/>
<!-- Svelte 5 | Card.svelte -->
<script>
	let { 
    	title, 
    	body, 
    	footer, 
    	...undeclaredProps 
    } = $props();
</script>

<article {...undeclaredProps}>
	<header>{title}</header>
	<div>{body}</div>
	<footer>{footer}</footer>
</article>

The $props Rune

<!-- Svelte 4 | App.svelte -->
<script>
	import Card from './Card.svelte';
</script>

<Card 
	title="Card Title" 
    body="Card Body." 
    footer="Card Footer."
/>
<!-- Svelte 4 | Card.svelte -->
<script>
	export let title;
	export let body;
	export let footer;
</script>

<article {...$$restProps}>
	<header>{title}</header>
	<div>{body}</div>
	<footer>{footer}</footer>
</article>

Universal Reactivity in Svelte

Universal Reactivity in Svelte

<!-- Svelte 5 -->
<script>
	let counter = $state(0);

	const inc = () => counter++;
	const dec = () => counter--;
</script>

<button onclick={dec}>-</button>
<output>{counter}</output>
<button onclick={inc}>+</button>

Universal Reactivity in Svelte

<!-- Svelte 5 -->
<script>
	function getCounter() {
    	let count = $state(0);

    	const inc = () => count++;
    	const dec = () => count--;

    	return {
        	get count() { return count },
        	inc,
        	dec
    	}
	}

	let counter = getCounter();
</script>

<button onclick={counter.dec}>-</button>
<output>{counter.count}</output>
<button onclick={counter.inc}>+</button>

Universal Reactivity in Svelte

<!-- Svelte 5 | App.svelte -->
<script>
	import { getCounter } from './counter.svelte.js'

	let counter = getCounter();
</script>

<button onclick={counter.dec}>-</button>
<output>{counter.value}</output>
<button onclick={counter.inc}>+</button>
// Svelte 5 | counter.svelte.js
export function getCounter() {
	let counter = $state(0);

	const inc = () => counter++;
	const dec = () => counter--;

	return {
    	get value() { return counter },
    	inc,
    	dec
	}
}

Universal Reactivity in Svelte

<!-- Svelte 5 | App.svelte -->
<script>
	import { Counter } from './counter.svelte.js';

	let counter = new Counter();
</script>

<button onclick={() => counter.dec()}>-</button>
<output>{counter.value}</output>
<button onclick={() => counter.inc()}>+</button>
// Svelte 5 | counter.svelte.js
export class Counter {
	value = $state(0);

	inc() { this.value++; }
	dec() { this.value--; }
}

Universal Reactivity in Svelte

<!-- Svelte 5 | App.svelte -->
<script>
	import { Counter } from './counter.svelte.js';

	let counter = new Counter();
</script>

<button onclick={() => counter.dec()}>-</button>
<output>{counter.value}</output>
<button onclick={() => counter.inc()}>+</button>
// Svelte 5 | counter.svelte.js
export class Counter {
	#value = $state(0);

	get value() { return this.#value }
    
	inc() { this.#value++; }
	dec() { this.#value--; }
}

Event Listeners in Svelte

Event Listeners in Svelte

<!-- Svelte 5 -->
<script>
	function onsubmit(event) {
    	event.preventDefault();
    	const formData = new FormData(event.target);
    	console.log(formData.get("name"));
	}

	const handleClick = () => console.log("submitted")
</script>

<form {onsubmit}>
	<input name="name" />
	<button onclick={handleClick}>Submit</button>
</form>

Event Listeners in Svelte

<!-- Svelte 4 -->
<script>
	function onsubmit(event) {
    	const formData = new FormData(event.target);
        console.log(formData.get("name"));
	}

	const handleClick = () => console.log("submitted")
</script>

<form on:submit|preventDefault={onsubmit}>
	<input name="name" />
	<button on:click={handleClick}>Submit</button>
</form>

Event Listeners in Svelte

<!-- Svelte 4 | App.svelte -->
<script>
	import Input from './Input.svelte'
</script>

<Input on:input={() => console.log("input")} />
<!-- Svelte 4 | Input.svelte -->
<!-- Every event has to be listed here in order to forward it! -->
<input {...$$restProps} on:input on:change on:keypress />

Event Listeners in Svelte

<!-- Svelte 5 | App.svelte -->
<script>
	import Input from './Input.svelte'
</script>

<Input oninput={() => console.log("input")} />
<!-- Svelte 5 | Input.svelte -->
<script>
	let {...restProps} = $props();
</script>

<input {...restProps} />

Snippets & Render Tags

Snippets & Render Tags

<!-- Svelte 5 -->
<script>
	let cards = [{ /*...*/ }, /* ... */];
</script>

{#snippet cardSnippet(title, content)}
	<article class="card">
    	<header>{title}</header>
    	<p>{content}</p>
	</article>
{/snippet}

{#each cards as card}
	{#if card.href}
    	<a href={card.href}>
			{@render cardSnippet(card.title, card.content)}
		</a>
	{:else}
    	{@render cardSnippet(card.title, card.content)}
	{/if}
{/each}

Snippets & Render Tags

<!-- Svelte 5 | CardList.svelte -->
<script>
	let cards = [{ /*...*/ }, /* ... */];

	let { titleSnippet, bodySnippet } = $props();
</script>

{#each cards as card}
	<article>
		<header>{@render titleSnippet(card.title)}</header>
		{@render bodySnippet(card.body)}
	</article>
{/each}
<!-- Svelte 5 | App.svelte -->
<script>
	import CardList from './CardList.svelte';
</script>

{#snippet titleSnippet(title)}
	<strong>{title}</strong>
{/snippet}

{#snippet bodySnippet(bodyCopy)}
	<div>{bodyCopy}</div>
{/snippet}

<CardList {titleSnippet} {bodySnippet} />

Snippets & Render Tags

<!-- Svelte 4 | CardList.svelte -->
<script>
	let cards = [{ /*...*/ }, /* ... */];
</script>

{#each cards as card}
	<article>
		<header>
			<slot name="header" title={card.title} />
		</header>
		<slot name="body" body={card.body} />
	</article>
{/each}
<!-- Svelte 4 | App.svelte -->
<script>
	import CardList from './CardList.svelte';
</script>

<CardList {titleSnippet}>
	<strong slot="header" let:title>{title}</strong>
	<svelte:fragment slot="body" let:body>
      {body}
  </svelte:fragment>
</CardList>

Quality of Life Improvements

  1. Seamless migration
  2. Full backward compatibility
  3. Dedicated CLI called sv

My Opinion on the Changes

Thank you very much!

 

X:

LinkedIn:

Xing:

Bluesky: