Nils Röhrig | REWE digital
Intro to
- What is Svelte?
- Where dose Svelte come from?
- What can Svelte do?
- What does Svelte do differently?
What is Svelte?
Core Attributes
- Component framework
- Encapsulation of UI Elements
- Declarative composition
UI Components

UI Components


UI Components



UI Components




UI Components





UI Components





Example Component
<script>
import Input from "./Input.svelte";
import Button from "./Button.svelte";
</script>
<form action="/login" method="post">
<Input type="email" label="E-Mail" name="email" />
<Input type="password" label="Password" name="password" />
<Button type="submit" label="Log in" />
</form>
Where does Svelte come from?
HTML & CSS
.block__element--modifier
<form action="/login" method="post" class="form form--login">
<div class="form__row">
<label for="email">E-Mail</label>
<input type="email" name="email" class="form__input form__input--email" />
</div>
<div class="form__row">
<label for="password">Password</label>
<input type="password" class="form__input form__input--password" />
</div>
<div class="form__row">
<button class="form__button form__button--submit">Submit</button>
</div>
</form>
.form {}
.form--login {}
.form__row {}
.form__input {}
.form__input--email {}
.form__input--password {}
.form__button {}
.form__button--submit {}
Bootstrap
<div class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title">Modal Example</h1>
<button class="btn-close" data-bs-dismiss="modal" />
</div>
<div class="modal-body">
<p>Example content.</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-bs-dismiss="modal">Ok</button>
</div>
</div>
</div>
</div>
JavaScript
Model-View-ViewModel




React
- Released in 2013 at Facebook
- Game changer
- No MVVM
- Immutability
- One-Way-Dataflow
- Virtual DOM
One-Way-Dataflow

Virtual DOM

Virtual DOM

Virtual DOM


Virtual DOM


And where does Svelte come in?
- Released 2016 by Rich Harris
- Influenced by it's predecessors
- Adoption of good aspects
- Refinement of less good aspects
- Initially a niche existence

What can Svelte do?
Svelte is a compiler
Svelte component
<h1>Hello Svelte Day!</h1>
Svelte component
JavaScript
Compiler
<h1>Hello Svelte Day!</h1>
/* HelloSvelteDay.svelte generated by Svelte v3.46.4 */
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 = "Hello Svelte Day!";
},
m(target, anchor) {
insert(target, h1, anchor);
},
p: noop,
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(h1);
},
};
}
class HelloSvelteDay extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, create_fragment, safe_not_equal, {});
}
}
export default HelloSvelteDay;
JavaScript
<h1>Hello Svelte Day!</h1>
/* HelloSvelteDay.svelte generated by Svelte v3.46.4 */
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 = "Hello Svelte Day!";
},
m(target, anchor) {
insert(target, h1, anchor);
},
p: noop,
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(h1);
},
};
}
class HelloSvelteDay extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, create_fragment, safe_not_equal, {});
}
}
export default HelloSvelteDay;
Svelte is a language
Structure of a component
<script>
// STATE & BEHAVIOR
</script>
<style>
/* PRESENTATION */
</style>
<!-- DECLARATIVE MARKUP -->
Directives
<script>
let value = 0;
const logValue = () => console.log(value)
</script>
<input bind:value type="number" />
<button on:click={logValue}>Log</button>
Control structures
<script>
let showFruits = false;
let fruits = ["banana", "apple", "orange"];
const toggleFruitVisibility = () => showFruits = !showFruits
</script>
<button on:click={toggleFruitVisibility}>Toggle Fruit Visibility</button>
{#if showFruits}
<ul>
{#each fruits as fruit}
<li>{fruit}</li>
{/each}
</ul>
{:else}
<p>Fruits are invisible, please toggle fruit visibility.</p>
{/if}
Reactivity
Original table

Manual update


Reactive update



Transitive reactive update




Reactive syntax
<script>
let value = 0
$: valueSquared = value * value
$: valueCubed = valueSquared * value
</script>
<label>
Value: <input bind:value type="number" />
</label>
Value squared: <output>{valueSquared}</output>
Value cubed: <output>{valueCubed}</output>
Event handling
Event handling
<script>
const handleAlways = () => console.log('clicked')
const handleOnce = () => console.log('clicked only once')
</script>
<button on:click={handleAlways}>Fires always</button><br>
<button on:click|once={handleOnce}>Fires only once</button>
State management
<script>
let count = 0;
const increment = () => count = count + 1
const decrement = () => count = count - 1
</script>
<button on:click={increment}>increment</button>
<button on:click={decrement}>decrement</button>
<output>{count}</output>
Local state
- Simple variables
- Direct access from component
- Assignment triggers update
- Caution with sets, arrays, etc.
Props
<!-- PropsContainer -->
<script>
import PropsDisplayer from "./PropsDisplayer.svelte"
</script>
<PropsDisplayer name="Svelte Day" />
<!-- PropsDisplayer -->
<script>
export let name
</script>
<p>Hello {name}!</p>
Props
<!-- PropsContainer -->
<script>
import PropsDisplayer from "./PropsDisplayer.svelte"
</script>
<PropsDisplayer name="Svelte Day" />
<!-- PropsDisplayer -->
<script>
export let name
</script>
<p>Hello {name}!</p>
Props
<!-- PropsContainer -->
<script>
import PropsDisplayer from "./PropsDisplayer.svelte"
</script>
<PropsDisplayer name="Svelte Day" />
<!-- PropsDisplayer -->
<script>
export let name
</script>
<p>Hello {name}!</p>
Props
<!-- PropsContainer -->
<script>
import PropsDisplayer from "./PropsDisplayer.svelte"
</script>
<PropsDisplayer name="Svelte Day" />
<!-- PropsDisplayer -->
<script>
export let name
</script>
<p>Hello {name}!</p>

Stores
- Publish/subscribe model
- Available everywhere
- Particular language support
- Reactive
Counter store
<script>
import { onDestroy } from "svelte"
import { writable } from "svelte/store"
let counterValue = 0
const counter = writable(0);
const unsubscribe = counter.subscribe(
(storeValue) => (counterValue = storeValue)
)
onDestroy(unsubscribe)
</script>
<button on:click={() => counter.update((v) => v - 1)}>decrement</button>
<button on:click={() => counter.update((v) => v + 1)}>increment</button>
<output>{counterValue}</output>
Language support for stores
<script>
import { writable } from "svelte/store";
const counter = writable(0);
</script>
<button on:click={() => counter.update((v) => v - 1)}>decrement</button>
<button on:click={() => counter.update((v) => v + 1)}>increment</button>
<output>{$counter}</output>
Stylesheets
Language support for stylesheets
<style>
h1 {
color: red;
}
.blue {
color: blue;
}
:global(h1) {
font-weight: normal;
}
</style>
<h1>Red Heading!</h1>
<h1 class="blue">Blue Heading!</h1>
What does Svelte do differently?
Omission of a virtual DOM
- Comparision between actual and target is expensive
- In Svelte unnecessary, because it's a compiler
- Changes already known at build time
Less code
React
Svelte
import React, { useState } from "react";
export default function Summing() {
const [operand1, setOperand1] = useState(0);
const [operand2, setOperand2] = useState(0);
return (
<>
<h1>Summing</h1>
<input
type="number"
value={operand1}
onChange={(e) => setOperand1(Number(e.target.value))}
/>
+
<input
type="number"
value={operand2}
onChange={(e) => setOperand2(Number(e.target.value))}
/>
=
<strong>{operand1 + operand2}</strong>
</>
);
}
<script>
let operand1 = 0;
let operand2 = 0;
</script>
<h1>Summing</h1>
<input type="number" bind:value={operand1} />
+
<input type="number" bind:value={operand2} />
=
<strong>{operand1 + operand2}</strong>
Less code
React
Svelte
import React, { useState } from "react";
export default function Summing() {
const [operand1, setOperand1] = useState(0);
const [operand2, setOperand2] = useState(0);
return (
<>
<h1>Summing</h1>
<input
type="number"
value={operand1}
onChange={(e) => setOperand1(Number(e.target.value))}
/>
+
<input
type="number"
value={operand2}
onChange={(e) => setOperand2(Number(e.target.value))}
/>
=
<strong>{operand1 + operand2}</strong>
</>
);
}
<script>
let operand1 = 0;
let operand2 = 0;
</script>
<h1>Summing</h1>
<input type="number" bind:value={operand1} />
+
<input type="number" bind:value={operand2} />
=
<strong>{operand1 + operand2}</strong>
Less code
React
Svelte
import React, { useState } from "react";
export default function Summing() {
const [operand1, setOperand1] = useState(0);
const [operand2, setOperand2] = useState(0);
return (
<>
<h1>Summing</h1>
<input
type="number"
value={operand1}
onChange={(e) => setOperand1(Number(e.target.value))}
/>
+
<input
type="number"
value={operand2}
onChange={(e) => setOperand2(Number(e.target.value))}
/>
=
<strong>{operand1 + operand2}</strong>
</>
);
}
<script>
let operand1 = 0;
let operand2 = 0;
</script>
<h1>Summing</h1>
<input type="number" bind:value={operand1} />
+
<input type="number" bind:value={operand2} />
=
<strong>{operand1 + operand2}</strong>
Less code
React
Svelte
import React, { useState } from "react";
export default function Summing() {
const [operand1, setOperand1] = useState(0);
const [operand2, setOperand2] = useState(0);
return (
<>
<h1>Summing</h1>
<input
type="number"
value={operand1}
onChange={(e) => setOperand1(Number(e.target.value))}
/>
+
<input
type="number"
value={operand2}
onChange={(e) => setOperand2(Number(e.target.value))}
/>
=
<strong>{operand1 + operand2}</strong>
</>
);
}
<script>
let operand1 = 0;
let operand2 = 0;
</script>
<h1>Summing</h1>
<input type="number" bind:value={operand1} />
+
<input type="number" bind:value={operand2} />
=
<strong>{operand1 + operand2}</strong>
Less code
React
Svelte
import React, { useState } from "react";
export default function Summing() {
const [operand1, setOperand1] = useState(0);
const [operand2, setOperand2] = useState(0);
return (
<>
<h1>Summing</h1>
<input
type="number"
value={operand1}
onChange={(e) => setOperand1(Number(e.target.value))}
/>
+
<input
type="number"
value={operand2}
onChange={(e) => setOperand2(Number(e.target.value))}
/>
=
<strong>{operand1 + operand2}</strong>
</>
);
}
<script>
let operand1 = 0;
let operand2 = 0;
</script>
<h1>Summing</h1>
<input type="number" bind:value={operand1} />
+
<input type="number" bind:value={operand2} />
=
<strong>{operand1 + operand2}</strong>
Feature comparision
Svelte | React | |
---|---|---|
CSS support | ✅ | ❌ |
Transitions | ✅ | ❌ |
Animation | ✅ | ❌ |
State management | ✅ | / |
Declarative acces to window, head or body | ✅ | ❌ |
Usable without a build step | ❌ | / |
Large ecosystem | / | ✅ |
Backed by a large company | ❌ | ✅ |
Proximity to platform
React
Svelte
import React, { useRef } from "react";
import tippy from "tippy.js";
export default function App() {
const tippyRef = useRef();
if (tippyRef != null) {
tippy(tippyRef.current, {
content: "Tippy Tooltip!",
});
}
return (
<>
<button ref={tippyRef}>Tippy Button</button>
</>
);
}
<script>
import tippy from "tippy.js";
</script>
<button use:tippy={{ content: 'Tippy Tooltip!' }}>
Tippy Button!
</button>
Proximity to platform
React
Svelte
import React, { useRef } from "react";
import tippy from "tippy.js";
export default function App() {
const tippyRef = useRef();
if (tippyRef != null) {
tippy(tippyRef.current, {
content: "Tippy Tooltip!",
});
}
return (
<>
<button ref={tippyRef}>Tippy Button</button>
</>
);
}
<script>
import tippy from "tippy.js";
</script>
<button use:tippy={{ content: 'Tippy Tooltip!' }}>
Tippy Button!
</button>
Proximity to platform
React
Svelte
import React, { useRef } from "react";
import tippy from "tippy.js";
export default function App() {
const tippyRef = useRef();
if (tippyRef != null) {
tippy(tippyRef.current, {
content: "Tippy Tooltip!",
});
}
return (
<>
<button ref={tippyRef}>Tippy Button</button>
</>
);
}
<script>
import tippy from "tippy.js";
</script>
<button use:tippy={{ content: 'Tippy Tooltip!' }}>
Tippy Button!
</button>
Learning curve
- HTML, CSS, JS are already halfway there
- Syntax extensions similar to template engines
- Specifics are relatively easy to pick up
- Work through the official tutorial
- Experiment within the REPL
- Try Svelte for a small portion of your next product or project
Thank you!
@drunknzombiecow
nils.roehrig@rewe-digital.com
Intro to Svelte (latest)
By Nils Röhrig
Intro to Svelte (latest)
- 2,068