Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 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
Expand Up @@ -4,15 +4,15 @@ title: Welcome to Svelte

Welcome to the Svelte tutorial! This will teach you everything you need to know to easily build web applications of all sizes, with high performance and a small footprint.

You can also consult the [API docs](https://svelte.dev/docs) and the [examples](https://svelte.dev/examples), or — if you're impatient to start hacking on your machine locally — create a project with `npm init svelte`.
You can also consult the [API docs](https://svelte.dev/docs) and visit the [playground](https://svelte.dev/playground), or — if you're impatient to start hacking on your machine locally — create a project with `npx sv create`.

## What is Svelte?

Svelte is a tool for building web applications. Like other user interface frameworks, it allows you to build your app _declaratively_ out of components that combine markup, styles and behaviours.

These components are _compiled_ into small, efficient JavaScript modules that eliminate overhead traditionally associated with UI frameworks.

You can build your entire app with Svelte (for example, using an application framework like [SvelteKit](https://kit.svelte.dev), which this tutorial will cover), or you can add it incrementally to an existing codebase. You can also ship components as standalone packages that work anywhere.
You can build your entire app with Svelte (for example, using an application framework like [SvelteKit](/docs/kit), which this tutorial will cover), or you can add it incrementally to an existing codebase. You can also ship components as standalone packages that work anywhere.

## How to use this tutorial

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ Our image is missing a `src` — let's add one:

That's better. But if you hover over the `<img>` in the editor, Svelte is giving us a warning:

> A11y: &lt;img&gt; element should have an alt attribute
```
`<img>` element should have an alt attribute
```

When building web apps, it's important to make sure that they're _accessible_ to the broadest possible userbase, including people with (for example) impaired vision or motion, or people without powerful hardware or good internet connections. Accessibility (shortened to a11y) isn't always easy to get right, but Svelte will help by warning you if you write inaccessible markup.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ Add a `<script>` tag to the top of `App.svelte` that imports `Nested.svelte`...

Notice that even though `Nested.svelte` has a `<p>` element, the styles from `App.svelte` don't leak in.

> Component names are always capitalised, to distinguish them from HTML elements.
> Component names are capitalised, to distinguish them from HTML elements.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
let count = 0;

function increment() {
// event handler code goes here
// TODO implement
}
</script>

<button>
<button onclick={increment}>
Clicked {count}
{count === 1 ? 'time' : 'times'}
</button>
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<script>
let count = 0;
let count = $state(0);

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

<button on:click={increment}>
<button onclick={increment}>
Clicked {count}
{count === 1 ? 'time' : 'times'}
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: State
---

At the heart of Svelte is a powerful system of _reactivity_ for keeping the DOM in sync with your application state — for example, in response to an event.

Make the `count` declaration reactive by wrapping the value with `$state(...)`:

```js
/// file: App.svelte
let count = +++$state(0)+++;
```

This is called a _rune_, and it's how you tell Svelte that `count` isn't an ordinary variable. Runes look like functions, but they're not — when you use Svelte, they're part of the language itself.

All that's left is to implement `increment`:

```js
/// file: App.svelte
function increment() {
+++count += 1;+++
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script>
let numbers = [1, 2, 3, 4];

function addNumber() {
// TODO implement
}
</script>

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

<button onclick={addNumber}>
Add a number
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script>
let numbers = $state([1, 2, 3, 4]);

function addNumber() {
numbers.push(numbers.length + 1);
}
</script>

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

<button onclick={addNumber}>
Add a number
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: Deep state
---

As we saw in the previous exercise, state reacts to _reassignments_. But it also reacts to _mutations_ — we call this _deep reactivity_.

Make `numbers` a reactive array:

```js
/// file: App.svelte
let numbers = +++$state([1, 2, 3, 4])+++;
```

Now, when we change the array...

```js
/// file: App.svelte
function addNumber() {
+++numbers[numbers.length] = numbers.length + 1;+++
}
```

...the component updates. Or better still, we can `push` to the array instead:

```js
/// file: App.svelte
function addNumber() {
+++numbers.push(numbers.length + 1);+++
}
```

> Deep reactivity is implemented using [proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), and mutations to the proxy do not affect the original object.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script>
let numbers = $state([1, 2, 3, 4]);
let total = $derived(numbers.reduce((t, n) => t + n, 0));

function addNumber() {
numbers.push(numbers.length + 1);
}
</script>

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

<button onclick={addNumber}>
Add a number
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
title: Derived state
---

Often, you will need to _derive_ state from other state. For this, we have the `$derived` rune:

```js
/// file: App.svelte
let numbers = $state([1, 2, 3, 4]);
+++let total = $derived(numbers.reduce((t, n) => t + n, 0));+++
```

We can now use this in our markup:

```svelte
/// file: App.svelte
<p>{numbers.join(' + ')} = +++{total}+++</p>
```

The expression inside the `$derived` declaration will be re-evaluated whenever its dependencies (in this case, just `numbers`) are updated. Unlike normal state, derived state is read-only.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
let elapsed = $state(0);
let interval = $state(1000);
</script>

<button onclick={() => interval /= 2}>speed up</button>
<button onclick={() => interval *= 2}>slow down</button>

<p>elapsed: {elapsed}</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script>
let elapsed = $state(0);
let interval = $state(1000);

$effect(() => {
const id = setInterval(() => {
elapsed += 1;
}, interval);

return () => {
clearInterval(id);
};
});
</script>

<button onclick={() => interval /= 2}>speed up</button>
<button onclick={() => interval *= 2}>slow down</button>

<p>elapsed: {elapsed}</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: Effects
---

So far we've talked about reactivity in terms of state. But that's only half of the equation — state is only reactive if something is _reacting_ to it, otherwise it's just a sparkling variable.

The thing that reacts is called an _effect_. You've already encountered effects — the ones that Svelte creates on your behalf to update the DOM in response to state changes — but you can also create your own with the `$effect` rune.

> Most of the time, you shouldn't. `$effect` is best thought of as an escape hatch, rather than something to use frequently. If you can put your side effects in an [event handler](dom-events), for example, that's almost always preferable.

Let's say we want to use `setInterval` to keep track of how long the component has been mounted. Create the effect:

```svelte
/// file: App.svelte
<script>
let elapsed = $state(0);
let interval = $state(1000);

+++ $effect(() => {
setInterval(() => {
elapsed += 1;
}, interval);
});+++
</script>
```

Click the 'speed up' button a few times and notice that `elapsed` ticks up faster, because we're calling `setInterval` each time `interval` gets smaller.

If we then click the 'slow down' button... well, it doesn't work. That's because we're not clearing out the old intervals when the effect updates. We can fix that by returning a cleanup function:

```js
/// file: App.svelte
$effect(() => {
+++const id =+++ setInterval(() => {
elapsed += 1;
}, interval);

+++ return () => {
clearInterval(id);
};+++
});
```

The cleanup function is called immediately before the effect function re-runs when `interval` changes, and also when the component is destroyed. (If the effect function doesn't read any state when it runs, it will only run once, when the component mounts.)

> Effects do not run during server-side rendering.
Loading
Loading