Nils Röhrig | REWE digital

Intro to

  1. What is Svelte?
  2. Where dose Svelte come from?
  3. What can Svelte do?
  4. 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
  1. Work through the official tutorial
  2. Experiment within the REPL
  3. Try Svelte for a small portion of your next product or project

Thank you!

@drunknzombiecow

nils.roehrig@rewe-digital.com