SvelteKit and Storyblok

Pixel Pioneers 2023

a Powerful Duo

Svelte and SvelteKit Primer

Scott Spence

- Svelte LDN meetup organiser

- Working with Svelte workshops

- Developer Relations Engineer

- Dad πŸ‘¨β€πŸ‘©β€πŸ‘§

- Cat dad 😺

What is Svelte?

What is Svelte?

- Component Framework

- No virtual DOM

- It's a compiler

- v1 released in late 2016

What's a framework?

- Declarative code

Declarative

vs

Imperative

Imperative

function count_button() {
  let count = 0;

  const button = document.createElement('button');
  button.textContent = `Clicks: ${count}`;

  button.addEventListener('click', () => {
    count += 1;
    button.textContent = `Clicks: ${count}`;
  });

  return button;
}

Declarative

<script>
  let count = 0;
</script>

<button on:click={() => count += 1}>
  Clicks: {count}
</button>
import React, { useState } from 'react';

export default function Component() {
  const [count, setCount] = useState(0);

  return <button onClick={() => setCount(count + 1)}>
    Clicks {count}
  </button>;
}
import React, { useState } from 'react';

export default function Example() {
  const [count, setCount] = useState(0);

  return <button onClick={() => setCount(count + 1)}>Clicks {count}</button>;
}
<script>
  let count = 0;
</script>

<button on:click={() => count += 1}>Clicks: {count}</button>

Svelte Basics

Svelte Basics

- It's a HTML superset

- Styling

- Reactivity

- Component props

- Logic

HTML superset

<h1>Hello world!</h1>
<script>
</script>

<h1>Hello world!</h1>

<style>
</style>

Styling

<p>This is a paragraph.</p>

<style>
  p {
    color: purple;
    font-family: 'Comic Sans MS', cursive;
    font-size: 2em;
  }
</style>

Reactivity

<script>
  let count = 0;
  $: doubled = count * 2;

  function handleClick() {
    count += 1;
  }
</script>

<button on:click={handleClick}>
  Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

<p>{count} doubled is {doubled}</p>
<script>
  let count = 0;
  $: doubled = count * 2;

  function handleClick() {
    count += 1;
  }
</script>

<button on:click={handleClick}>
  Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

<p>{count} doubled is {doubled}</p>

What the flip?

$:

Gotchas?

Mutation

<script>
  let numbers = [1, 2, 3, 4];

  function addNumber() {
    numbers.push(numbers.length + 1);
  }

  $: sum = numbers.reduce((t, n) => t + n, 0);
</script>

<p>{numbers.join(' + ')} = {sum}</p>

<button on:click={addNumber}>
  Add a number
</button>

Assignment

<script>
  let numbers = [1, 2, 3, 4];

  function addNumber() {
    numbers = [...numbers, numbers.length + 1];
  }

  $: sum = numbers.reduce((t, n) => t + n, 0);
</script>

<p>{numbers.join(' + ')} = {sum}</p>

<button on:click={addNumber}>
  Add a number
</button>

Logic

- #if 
- #else 
- #each
- #await

Logic blocks

#
/
:

opening tag

closing tag

continuation tag

#if
<script>
  let user = { loggedIn: false };

  function toggle() {
    user.loggedIn = !user.loggedIn;
  }
</script>

<button on:click={toggle}>
  Log out
</button>

<button on:click={toggle}>
  Log in
</button>
<script>
  let user = { loggedIn: false };

  function toggle() {
    user.loggedIn = !user.loggedIn;
  }
</script>

{#if user.loggedIn}
  <button on:click={toggle}>
    Log out
  </button>
{/if}

{#if !user.loggedIn}
  <button on:click={toggle}>
    Log in
  </button>
{/if}
#else
<script>
  let user = { loggedIn: false };

  function toggle() {
    user.loggedIn = !user.loggedIn;
  }
</script>

{#if user.loggedIn}
  <button on:click={toggle}>
    Log out
  </button>
{/if}

{#if !user.loggedIn}
  <button on:click={toggle}>
    Log in
  </button>
{/if}
<script>
  let user = { loggedIn: false };

  function toggle() {
    user.loggedIn = !user.loggedIn;
  }
</script>

{#if user.loggedIn}
  <button on:click={toggle}>
    Log out
  </button>
{:else}
  <button on:click={toggle}>
    Log in
  </button>
{/if}
<script>
  let user = { loggedIn: false };

  function toggle() {
    user.loggedIn = !user.loggedIn;
  }
</script>

{#if user.loggedIn}
  <button on:click={toggle}>
    Log out
  </button>
{/if}

{#if !user.loggedIn}
  <button on:click={toggle}>
    Log in
  </button>
{/if}
<script>
  let user = { loggedIn: false };

  function toggle() {
    user.loggedIn = !user.loggedIn;
  }
</script>

{#if user.loggedIn}
  <button on:click={toggle}>
    Log out
  </button>
{:else}
  <button on:click={toggle}>
    Log in
  </button>
{/if}
#each
<script>
  let speakers = [
    {handle: 'Rich_Harris', name: 'Rich Harris'},
    {handle: 'Biilmann', name: 'Matt Biilmann'},
    {handle: 'AishaBlake', name: 'Aisha Blake'},
  ]
</script>

<ul>
  {#each speakers as speaker, i}
    <li>
      <a target="_blank" href="https://twitter.com/{speaker.handle}">
        {i+1}: {speaker.name}
      </a>
    </li>
  {/each}
</ul>
<script>
  let speakers = [
    {handle: 'Rich_Harris', name: 'Rich Harris'},
    {handle: 'Biilmann', name: 'Matt Biilmann'},
    {handle: 'AishaBlake', name: 'Aisha Blake'},
  ]
</script>

<ul>
  {#each speakers as speaker, i}
    <li>
      <a target="_blank" href="https://twitter.com/{speaker.handle}">
        {i+1}: {speaker.name}
      </a>
    </li>
  {/each}
</ul>
#await
<script>
  async function getRandomNumber() {
    const res = await fetch(`/tutorial/random-number`);
    const text = await res.text();
    if (res.ok) {
      return text;
    } else {
      throw new Error(text);
    }
  }
  let promise = getRandomNumber();
</script>

<button on:click={() => (promise = getRandomNumber())}>
  generate random number
</button>

{#await promise}
  <p>...waiting</p>
{:then number}
  <p>The number is {number}</p>
{:catch error}
  <p style="color: red">{error.message}</p>
{/await}
<script>
  async function getRandomNumber() {
    const res = await fetch(`/tutorial/random-number`);
    const text = await res.text();
    if (res.ok) {
      return text;
    } else {
      throw new Error(text);
    }
  }
  let promise = getRandomNumber();
</script>

<button on:click={() => (promise = getRandomNumber())}>
  generate random number
</button>

{#await promise}
  <p>...waiting</p>
{:then number}
  <p>The number is {number}</p>
{:catch error}
  <p style="color: red">{error.message}</p>
{/await}

What is SvelteKit?

What is SvelteKit?

- Powerful router

- SSR and CSR support

- Page pre-loading

- HMR thanks to Vite

- a11y first framework

SvelteKit is to Svelte as NextJS is to React and Nuxt is to Vue

SvelteKit

meta-framework

➑️➑️

➑️➑️

➑️➑️

Understanding the Magic Behind Data Loading

The Curse of Knowledge

Routing

in SvelteKitΒ 

SvelteKit Routing

β”œβ”€β”€ src
β”‚   β”œβ”€β”€ routes
β”‚   β”‚   └── index.svelte
β”‚   β”œβ”€β”€ app.css
β”‚   └── app.html
β”œβ”€β”€ src
β”‚   β”œβ”€β”€ routes
β”‚   β”‚   β”œβ”€β”€ about.svelte
β”‚   β”‚   └── index.svelte
β”‚   β”œβ”€β”€ app.css
β”‚   β”œβ”€β”€ app.d.ts
β”‚   └── app.html

File Notation

β”œβ”€β”€ src
β”‚Β Β  β”œβ”€β”€ routes
β”‚Β Β  β”‚Β Β  └── +page.svelte
β”‚Β Β  β”œβ”€β”€ app.css
β”‚Β Β  └── app.html
β”œβ”€β”€ src
β”‚Β Β  β”œβ”€β”€ routes
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ about
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── +page.svelte
β”‚Β Β  β”‚Β Β  └── +page.svelte
β”‚Β Β  β”œβ”€β”€ app.css
β”‚Β Β  β”œβ”€β”€ app.d.ts
β”‚Β Β  └── app.html
β”œβ”€β”€ src
β”‚   β”œβ”€β”€ routes
β”‚   β”‚   β”œβ”€β”€ about
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ +page.svelte
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── +page.ts
β”‚   β”‚   └── +page.svelte
β”‚   β”œβ”€β”€ app.css
β”‚   β”œβ”€β”€ app.d.ts
β”‚   └── app.html
+page.svelte
+page.ts
+page.server.ts
+layout.svelte
+layout.ts
+layout.server.ts
+error.svelte
+server.ts
<script lang="ts">
  export let name = 'World'
</script>

<p>Hello, {name}!</p>

Component props

src/lib/components/greeting.svelte
<script lang="ts">
  import Greeting from '$lib/components/greeting.svelte'
</script>

<Greeting name="Svelte" />

Component props

src/routes/greet/+page.svelte
export const load = () => {
  return {
    title: 'This data is from the load function',
  }
}
src/routes/about/+page.ts
<script lang="ts">
  export let data
</script>

<h1>This is the about page</h1>

<pre>{JSON.stringify(data, null, 2)}</pre>
src/routes/about/+page.svelte
{
  "title": "This data is from the load function"
}
src/routes/about/+page.svelte (output)
<script lang="ts">
  export let data
</script>

<h1>This is the about page</h1>

<p>{data.title}</p>
src/routes/about/+page.svelte
This data is from the load function
src/routes/about/+page.svelte (output)

Understanding the Magic Behind Data Loading

πŸ˜…

Practical examples

Demo time

spences10

End

Thank youπŸ™