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π
Pixel Pioneers 2023
By Scott Spence
Pixel Pioneers 2023
- 236