Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
import Counter from './Counter.svelte';
</script>

<Counter />
<Counter />
<Counter />
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
import { count } from './shared.js';
</script>

<button onclick={() => {}}>
clicks: {count}
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export let count = 0;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
import { count } from './shared.js';
</script>

<button onclick={() => $count += 1}>
clicks: {$count}
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { writable } from 'svelte/store';

export let count = writable(0);
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: Introducing stores
---

Prior to the introduction of runes in Svelte 5, stores were the idiomatic way to handle reactive state outside components. That's no longer the case, but you'll still encounter stores when using Svelte (including in SvelteKit, for now), so it's worth knowing how to use them.

> [!NOTE] We won't cover how to create your own custom stores — for that, [consult the documentation](/docs/svelte/stores).

Let's revisit the example from the [universal reactivity](universal-reactivity) exercise, but this time implement the shared state using a store.

In `shared.js` we're currently exporting `count`, which is a number. Turn it into a writable store:

```js
/// file: shared.js
+++import { writable } from 'svelte/store';+++

export const count = +++writable(0)+++;
```

To reference the value of the store, we prefix it with a `$` symbol. In `Counter.svelte`, update the text inside the `<button>` so that it no longer says `[object Object]`:

```svelte
/// file: Counter.svelte
<button onclick={() => {}}>
clicks: {+++$count+++}
</button>
```

Finally, add the event handler. Because this is a writable store, we can update the value programmatically using its `set` or `update` method...

```js
count.update((n) => n + 1);
```

...but since we're in a component we can continue using the `$` prefix:

```svelte
/// file: Counter.svelte
<button onclick={() => +++$count += 1+++}>
clicks: {$count}
</button>
```
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
<script>
import { writable } from 'svelte/store';

const progress = writable(0);
const progress = writable(0.5);
</script>

<progress value={$progress}></progress>

<button on:click={() => progress.set(0)}>
<button onclick={() => progress.set(0)}>
0%
</button>

<button on:click={() => progress.set(0.25)}>
<button onclick={() => progress.set(0.25)}>
25%
</button>

<button on:click={() => progress.set(0.5)}>
<button onclick={() => progress.set(0.5)}>
50%
</button>

<button on:click={() => progress.set(0.75)}>
<button onclick={() => progress.set(0.75)}>
75%
</button>

<button on:click={() => progress.set(1)}>
<button onclick={() => progress.set(1)}>
100%
</button>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,31 @@
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';

const progress = tweened(0, {
const progress = tweened(0.5, {
duration: 400,
easing: cubicOut
});
</script>

<progress value={$progress}></progress>

<button on:click={() => progress.set(0)}>
<button onclick={() => progress.set(0)}>
0%
</button>

<button on:click={() => progress.set(0.25)}>
<button onclick={() => progress.set(0.25)}>
25%
</button>

<button on:click={() => progress.set(0.5)}>
<button onclick={() => progress.set(0.5)}>
50%
</button>

<button on:click={() => progress.set(0.75)}>
<button onclick={() => progress.set(0.75)}>
75%
</button>

<button on:click={() => progress.set(1)}>
<button onclick={() => progress.set(1)}>
100%
</button>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
---
title: Tweens
title: Tweened values
---

Now that we've covered the basics, it's time to learn some advanced Svelte techniques, starting with _motion_.

Setting values and watching the DOM update automatically is cool. Know what's even cooler? Tweening those values. Svelte includes tools to help you build slick user interfaces that use animation to communicate changes.
Alongside the `writable` and `readable` stores, Svelte ships stores for adding motion to your user interfaces.

Let's start by changing the `progress` store to a `tweened` store:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
</script>

<svg
on:mousemove={(e) => {
onmousemove={(e) => {
coords.set({ x: e.clientX, y: e.clientY });
}}
on:mousedown={() => size.set(30)}
on:mouseup={() => size.set(10)}
onmousedown={() => size.set(30)}
onmouseup={() => size.set(10)}
role="presentation"
>
<circle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
</script>

<svg
on:mousemove={(e) => {
onmousemove={(e) => {
coords.set({ x: e.clientX, y: e.clientY });
}}
on:mousedown={() => size.set(30)}
on:mouseup={() => size.set(10)}
onmousedown={() => size.set(30)}
onmouseup={() => size.set(10)}
role="presentation"
>
<circle
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Motion
title: Stores
scope: { 'prefix': '/src/lib/', 'name': 'src' }
focus: /src/lib/App.svelte
---
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script>
let html = '<p>Write some text!</p>';
let html = $state('<p>Write some text!</p>');
</script>

<div contenteditable></div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script>
let html = '<p>Write some text!</p>';
let html = $state('<p>Write some text!</p>');
</script>

<div bind:innerHTML={html} contenteditable></div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<script>
let todos = [
let todos = $state([
{ done: false, text: 'finish Svelte tutorial' },
{ done: false, text: 'build an app' },
{ done: false, text: 'world domination' }
];
]);

function add() {
todos = todos.concat({
todos.push({
done: false,
text: ''
});
Expand All @@ -16,7 +16,7 @@
todos = todos.filter((t) => !t.done);
}

$: remaining = todos.filter((t) => !t.done).length;
let remaining = $derived(todos.filter((t) => !t.done).length);
</script>

<div class="centered">
Expand All @@ -41,11 +41,11 @@

<p>{remaining} remaining</p>

<button on:click={add}>
<button onclick={add}>
Add new
</button>

<button on:click={clear}>
<button onclick={clear}>
Clear completed
</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<script>
let todos = [
let todos = $state([
{ done: false, text: 'finish Svelte tutorial' },
{ done: false, text: 'build an app' },
{ done: false, text: 'world domination' }
];
]);

function add() {
todos = todos.concat({
todos.push({
done: false,
text: ''
});
Expand All @@ -16,7 +16,7 @@
todos = todos.filter((t) => !t.done);
}

$: remaining = todos.filter((t) => !t.done).length;
let remaining = $derived(todos.filter((t) => !t.done).length);
</script>

<div class="centered">
Expand All @@ -41,11 +41,11 @@

<p>{remaining} remaining</p>

<button on:click={add}>
<button onclick={add}>
Add new
</button>

<button on:click={clear}>
<button onclick={clear}>
Clear completed
</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Each block bindings
---

You can even bind to properties inside an `each` block.
You can bind to properties inside an `each` block.

```svelte
/// file: App.svelte
Expand All @@ -21,5 +21,3 @@ You can even bind to properties inside an `each` block.
</li>
{/each}
```

> [!NOTE] Note that interacting with these `<input>` elements will mutate the array. If you prefer to work with immutable data, you should avoid these bindings and use event handlers instead.
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<script>
export let src;
export let title;
export let artist;
let { src, title, artist } = $props();

let time = 0;
let duration = 0;
let paused = true;
let time = $state(0);
let duration = $state(0);
let paused = $state(true);

function format(time) {
if (isNaN(time)) return '...';
Expand Down Expand Up @@ -33,16 +31,16 @@
<span>{format(time)}</span>
<div
class="slider"
on:pointerdown={e => {
onpointerdown={e => {
const div = e.currentTarget;

function seek(e) {
const { left, width } = div.getBoundingClientRect();

let p = (e.clientX - left) / width;
if (p < 0) p = 0;
if (p > 1) p = 1;

// TODO update the time
}

Expand Down Expand Up @@ -82,15 +80,15 @@
color: var(--fg-1);
filter: drop-shadow(0.5em 0.5em 1em rgba(0,0,0,0.1));
}

button {
width: 100%;
aspect-ratio: 1;
background-repeat: no-repeat;
background-position: 50% 50%;
border-radius: 50%;
}

[aria-label="pause"] {
background-image: url(./pause.svg);
}
Expand Down Expand Up @@ -133,4 +131,4 @@
height: 100%;
background: var(--bg-3);
}
</style>
</style>
Loading
Loading