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!
Nils Röhrig | Loql
Core characteristics of Svelte
Runes & Reactivity
Event Listeners
Snippets
My Opinion on the Changes
Core characteristics of Svelte
Runes & Reactivity
Event Listeners
Snippets
Quality of Life Features
My Opinion on the Changes
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.
Logo
Register
Home
E-Mail
Submit
etc. pp...
Component
Component
Component
<h1>Simple Component</h1>
Component.svelte
<h1>Simple Component</h1>
<h1>Simple Component</h1>
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;
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
<h1>Simple Component</h1>
import { mount } from "svelte";
import Component from "./Component.js";
mount(Component, {
target: document.querySelector("#root")
});
main.js
<!-- 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
{#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}
<script>
let name = $state("world");
</script>
<h1>Hello {name}!</h1>
Enter your name: <input bind:value={name} />
<!-- 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>
<!--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)
};
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 5 -->
<script>
let name = $state("world");
</script>
<h1>Hello {name}!</h1>
Enter your name: <input bind:value={name} />
<!-- Svelte 4 -->
<script>
let name = "world";
</script>
<h1>Hello {name}!</h1>
Enter your name: <input bind:value={name} />
<!-- 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} />
<!-- 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>
<!-- 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>
<!-- Svelte 5 -->
<script>
let quantity = $state(0);
$effect(() => {
console.log(`You've entered ${quantity}.`);
});
</script>
Quantity: <input bind:value={quantity} type="number" />
<!-- 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 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>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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
}
}
<!-- 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--; }
}
<!-- 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--; }
}
<!-- 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>
<!-- 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>
<!-- 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 />
<!-- 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} />
<!-- 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}
<!-- 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} />
<!-- 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>
X:
LinkedIn:
Xing:
Bluesky: