mirror of https://github.com/sveltejs/svelte
chore: restructure docs for new site (#13699)
parent
ad578a5da5
commit
fd78385447
@ -0,0 +1,66 @@
|
||||
---
|
||||
title: .svelte files
|
||||
---
|
||||
|
||||
Components are the building blocks of Svelte applications. They are written into `.svelte` files, using a superset of HTML.
|
||||
|
||||
All three sections — script, styles and markup — are optional.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```svelte
|
||||
/// file: MyComponent.svelte
|
||||
<script module>
|
||||
// module-level logic goes here
|
||||
// (you will rarely use this)
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// instance-level logic goes here
|
||||
</script>
|
||||
|
||||
<!-- markup (zero or more items) goes here -->
|
||||
|
||||
<style>
|
||||
/* styles go here */
|
||||
</style>
|
||||
```
|
||||
|
||||
## `<script>`
|
||||
|
||||
A `<script>` block contains JavaScript (or TypeScript, when adding the `lang="ts"` attribute) that runs when a component instance is created. Variables declared (or imported) at the top level can be referenced in the component's markup.
|
||||
|
||||
In addition to normal JavaScript, you can use _runes_ to declare [component props]($props) and add reactivity to your component. Runes are covered in the next section.
|
||||
|
||||
<!-- TODO describe behaviour of `export` -->
|
||||
|
||||
## `<script module>`
|
||||
|
||||
A `<script>` tag with a `module` attribute runs once when the module first evaluates, rather than for each component instance. Variables declared in this block can be referenced elsewhere in the component, but not vice versa.
|
||||
|
||||
```svelte
|
||||
<script module>
|
||||
let total = 0;
|
||||
</script>
|
||||
|
||||
<script>
|
||||
total += 1;
|
||||
console.log(`instantiated ${total} times`);
|
||||
</script>
|
||||
```
|
||||
|
||||
You can `export` bindings from this block, and they will become exports of the compiled module. You cannot `export default`, since the default export is the component itself.
|
||||
|
||||
## `<style>`
|
||||
|
||||
CSS inside a `<style>` block will be scoped to that component.
|
||||
|
||||
```svelte
|
||||
<style>
|
||||
p {
|
||||
/* this will only affect <p> elements in this component */
|
||||
color: burlywood;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
For more information regarding styling, read the documentation around [styles and classes](styles-and-classes).
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: .svelte.js and .svelte.ts files
|
||||
---
|
||||
|
||||
Besides `.svelte` files, Svelte also operates on `.svelte.js` and `.svelte.ts` files.
|
||||
|
||||
These behave like any other `.js` or `.ts` module, except that you can use runes. This is useful for creating reusable reactive logic, or sharing reactive state across your app.
|
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: What are runes?
|
||||
---
|
||||
|
||||
> [!NOTE] **rune** /ro͞on/ _noun_
|
||||
>
|
||||
> A letter or mark used as a mystical or magic symbol.
|
||||
|
||||
Runes are symbols that you use in `.svelte` and `.svelte.js`/`.svelte.ts` files to control the Svelte compiler. If you think of Svelte as a language, runes are part of the syntax — they are _keywords_.
|
||||
|
||||
Runes have a `$` prefix and look like functions:
|
||||
|
||||
```js
|
||||
let message = $state('hello');
|
||||
```
|
||||
|
||||
They differ from normal JavaScript functions in important ways, however:
|
||||
|
||||
- You don't need to import them — they are part of the language
|
||||
- They're not values — you can't assign them to a variable or pass them as arguments to a function
|
||||
- Just like JavaScript keywords, they are only valid in certain positions (the compiler will help you if you put them in the wrong place)
|
@ -0,0 +1,127 @@
|
||||
---
|
||||
title: $state
|
||||
---
|
||||
|
||||
The `$state` rune allows you to create _reactive state_, which means that your UI _reacts_ when it changes.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = $state(0);
|
||||
</script>
|
||||
|
||||
<button onclick={() => count++}>
|
||||
clicks: {count}
|
||||
</button>
|
||||
```
|
||||
|
||||
Unlike other frameworks you may have encountered, there is no API for interacting with state — `count` is just a number, rather than an object or a function, and you can update it like you would update any other variable.
|
||||
|
||||
### Deep state
|
||||
|
||||
If `$state` is used with an array or a simple object, the result is a deeply reactive _state proxy_. [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) allow Svelte to run code when you read or write properties, including via methods like `array.push(...)`, triggering granular updates.
|
||||
|
||||
State is proxified recursively until Svelte finds something other than an array or simple object. In a case like this...
|
||||
|
||||
```js
|
||||
let todos = $state([
|
||||
{
|
||||
done: false,
|
||||
text: 'add more todos'
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
...modifying an individual todo's property will trigger updates to anything in your UI that depends on that specific property:
|
||||
|
||||
```js
|
||||
// @filename: ambient.d.ts
|
||||
declare global {
|
||||
const todos: Array<{ done: boolean, text: string }>
|
||||
}
|
||||
|
||||
// @filename: index.js
|
||||
// ---cut---
|
||||
todos[0].done = !todos[0].done;
|
||||
```
|
||||
|
||||
If you push a new object to the array, it will also be proxified:
|
||||
|
||||
```js
|
||||
// @filename: ambient.d.ts
|
||||
declare global {
|
||||
const todos: Array<{ done: boolean, text: string }>
|
||||
}
|
||||
|
||||
// @filename: index.js
|
||||
// ---cut---
|
||||
todos.push({
|
||||
done: false,
|
||||
text: 'eat lunch'
|
||||
});
|
||||
```
|
||||
|
||||
> [!NOTE] When you update properties of proxies, the original object is _not_ mutated.
|
||||
|
||||
### Classes
|
||||
|
||||
You can also use `$state` in class fields (whether public or private):
|
||||
|
||||
```js
|
||||
// @errors: 7006 2554
|
||||
class Todo {
|
||||
done = $state(false);
|
||||
text = $state();
|
||||
|
||||
constructor(text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.text = '';
|
||||
this.done = false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE] The compiler transforms `done` and `text` into `get`/`set` methods on the class prototype referencing private fields.
|
||||
|
||||
## `$state.raw`
|
||||
|
||||
In cases where you don't want objects and arrays to be deeply reactive you can use `$state.raw`.
|
||||
|
||||
State declared with `$state.raw` cannot be mutated; it can only be _reassigned_. In other words, rather than assigning to a property of an object, or using an array method like `push`, replace the object or array altogether if you'd like to update it:
|
||||
|
||||
```js
|
||||
let person = $state.raw({
|
||||
name: 'Heraclitus',
|
||||
age: 49
|
||||
});
|
||||
|
||||
// this will have no effect
|
||||
person.age += 1;
|
||||
|
||||
// this will work, because we're creating a new person
|
||||
person = {
|
||||
name: 'Heraclitus',
|
||||
age: 50
|
||||
};
|
||||
```
|
||||
|
||||
This can improve performance with large arrays and objects that you weren't planning to mutate anyway, since it avoids the cost of making them reactive. Note that raw state can _contain_ reactive state (for example, a raw array of reactive objects).
|
||||
|
||||
## `$state.snapshot`
|
||||
|
||||
To take a static snapshot of a deeply reactive `$state` proxy, use `$state.snapshot`:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let counter = $state({ count: 0 });
|
||||
|
||||
function onclick() {
|
||||
// Will log `{ count: ... }` rather than `Proxy { ... }`
|
||||
console.log($state.snapshot(counter));
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
This is handy when you want to pass some state to an external library or API that doesn't expect a proxy, such as `structuredClone`.
|
@ -0,0 +1,45 @@
|
||||
---
|
||||
title: $derived
|
||||
---
|
||||
|
||||
Derived state is declared with the `$derived` rune:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = $state(0);
|
||||
let doubled = $derived(count * 2);
|
||||
</script>
|
||||
|
||||
<button onclick={() => count++}>
|
||||
{doubled}
|
||||
</button>
|
||||
|
||||
<p>{count} doubled is {doubled}</p>
|
||||
```
|
||||
|
||||
The expression inside `$derived(...)` should be free of side-effects. Svelte will disallow state changes (e.g. `count++`) inside derived expressions.
|
||||
|
||||
As with `$state`, you can mark class fields as `$derived`.
|
||||
|
||||
## `$derived.by`
|
||||
|
||||
Sometimes you need to create complex derivations that don't fit inside a short expression. In these cases, you can use `$derived.by` which accepts a function as its argument.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let numbers = $state([1, 2, 3]);
|
||||
let total = $derived.by(() => {
|
||||
let total = 0;
|
||||
for (const n of numbers) {
|
||||
total += n;
|
||||
}
|
||||
return total;
|
||||
});
|
||||
</script>
|
||||
|
||||
<button onclick={() => numbers.push(numbers.length + 1)}>
|
||||
{numbers.join(' + ')} = {total}
|
||||
</button>
|
||||
```
|
||||
|
||||
In essence, `$derived(expression)` is equivalent to `$derived.by(() => expression)`.
|
@ -0,0 +1,128 @@
|
||||
---
|
||||
title: $props
|
||||
---
|
||||
|
||||
The inputs to a component are referred to as _props_, which is short for _properties_. You pass props to components just like you pass attributes to elements:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import MyComponent from './MyComponent.svelte';
|
||||
</script>
|
||||
|
||||
/// file: App.svelte
|
||||
<MyComponent adjective="cool" />
|
||||
```
|
||||
|
||||
On the other side, inside `MyComponent.svelte`, we can receive props with the `$props` rune...
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let props = $props();
|
||||
</script>
|
||||
|
||||
/// file: MyComponent.svelte
|
||||
<p>this component is {props.adjective}</p>
|
||||
```
|
||||
|
||||
...though more commonly, you'll [_destructure_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) your props:
|
||||
|
||||
```svelte
|
||||
/// file: MyComponent.svelte
|
||||
<script>
|
||||
let +++{ adjective }+++ = $props();
|
||||
</script>
|
||||
|
||||
<p>this component is {+++adjective+++}</p>
|
||||
```
|
||||
|
||||
## Fallback values
|
||||
|
||||
Destructuring allows us to declare fallback values, which are used if the parent component does not set a given prop:
|
||||
|
||||
```js
|
||||
/// file: MyComponent.svelte
|
||||
let { adjective = 'happy' } = $props();
|
||||
```
|
||||
|
||||
> [!NOTE] Fallback values are not turned into reactive state proxies.
|
||||
|
||||
## Renaming props
|
||||
|
||||
We can also use the destructuring assignment to rename props, which is necessary if they're invalid identifiers, or a JavaScript keyword like `super`:
|
||||
|
||||
```js
|
||||
let { super: trouper = 'lights are gonna find me' } = $props();
|
||||
```
|
||||
|
||||
## Rest props
|
||||
|
||||
Finally, we can use a _rest property_ to get, well, the rest of the props:
|
||||
|
||||
```js
|
||||
let { a, b, c, ...others } = $props();
|
||||
```
|
||||
|
||||
## Updating props
|
||||
|
||||
References to a prop inside a component update when the prop itself updates — when `count` changes in `App.svelte`, it will also change inside `Child.svelte`. But the child component is able to temporarily override the prop value, which can be useful for unsaved emphemeral state ([demo](/playground/untitled#H4sIAAAAAAAAE6WQ0WrDMAxFf0WIQR0Wmu3VTQJln7HsIfVcZubIxlbGRvC_DzuBraN92qPula50tODZWB1RPi_IX16jLALWSOOUq6P3-_ihLWftNEZ9TVeOWBNHlNhGFYznfqCBzeRdYHh6M_YVzsFNsNs3pdpGd4eBcqPVDMrNxNDBXeSRtXioDgO1zU8ataeZ2RE4Utao924RFXQ9iHXwvoPHKpW1xY4g_Bg0cSVhKS0p560Za95612ZC02ONrD8ZJYdZp_rGQ37ff_mSP86Np2TWZaNNmdcH56P4P67K66_SXoK9pG-5dF5Z9QEAAA==)):
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```svelte
|
||||
/// file: App.svelte
|
||||
<script>
|
||||
import Child from './Child.svelte';
|
||||
|
||||
let count = $state(0);
|
||||
</script>
|
||||
|
||||
<button onclick={() => (count += 1)}>
|
||||
clicks (parent): {count}
|
||||
</button>
|
||||
|
||||
<Child {count} />
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```svelte
|
||||
/// file: Child.svelte
|
||||
<script>
|
||||
let { count } = $props();
|
||||
</script>
|
||||
|
||||
<button onclick={() => (count += 1)}>
|
||||
clicks (child): {count}
|
||||
</button>
|
||||
```
|
||||
|
||||
## Type safety
|
||||
|
||||
You can add type safety to your components by annotating your props, as you would with any other variable declaration. In TypeScript that might look like this...
|
||||
|
||||
```svelte
|
||||
<script lang="ts">
|
||||
let { adjective }: { adjective: string } = $props();
|
||||
</script>
|
||||
```
|
||||
|
||||
...while in JSDoc you can do this:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
/** @type {{ adjective: string }} */
|
||||
let { adjective } = $props();
|
||||
</script>
|
||||
```
|
||||
|
||||
You can, of course, separate the type declaration from the annotation:
|
||||
|
||||
```svelte
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
adjective: string;
|
||||
}
|
||||
|
||||
let { adjective }: Props = $props();
|
||||
</script>
|
||||
```
|
||||
|
||||
Adding types is recommended, as it ensures that people using your component can easily discover which props they should provide.
|
@ -0,0 +1,54 @@
|
||||
---
|
||||
title: $bindable
|
||||
---
|
||||
|
||||
Ordinarily, props go one way, from parent to child. This makes it easy to understand how data flows around your app.
|
||||
|
||||
In Svelte, component props can be _bound_, which means that data can also flow _up_ from child to parent. This isn't something you should do often, but it can simplify your code if used sparingly and carefully.
|
||||
|
||||
It also means that a state proxy can be _mutated_ in the child.
|
||||
|
||||
> [!NOTE] Mutation is also possible with normal props, but is strongly discouraged — Svelte will warn you if it detects that a component is mutating state it does not 'own'.
|
||||
|
||||
To mark a prop as bindable, we use the `$bindable` rune:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```svelte
|
||||
/// file: FancyInput.svelte
|
||||
<script>
|
||||
let { value = $bindable(), ...props } = $props();
|
||||
</script>
|
||||
|
||||
<input bind:value={value} {...props} />
|
||||
|
||||
<style>
|
||||
input {
|
||||
font-family: 'Comic Sans MS';
|
||||
color: deeppink;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
Now, a component that uses `<FancyInput>` can add the [`bind:`](bind) directive ([demo](/playground/untitled#H4sIAAAAAAAAE3WQwWrDMBBEf2URBSfg2nfFMZRCoYeecqx6UJx1IyqvhLUONcb_XqSkTUOSk1az7DBvJtEai0HI90nw6FHIJIhckO7i78n7IhzQctS2OuAtvXHESByEFFVoeuO5VqTYdN71DC-amvGV_MDQ9q6DrCjP0skkWymKJxYZOgxBfyKs4SGwZlxke7TWZcuVoqo8-1P1z3lraCcP2g64nk4GM5S1osrXf0JV-lrkgvGbheR-wDm_g30V8JL-1vpOCZFogpQsEsWcemtxscyhKArfOx9gjps0Lq4hzRVfemaYfu-PoIqqwKPFY_XpaIqj4tYRP7a6M3aUkD27zjSw0RTgbZN6Z8WNs66XsEP03tBXUueUJFlelvYx_wCuI3leNwIAAA==)):
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```svelte
|
||||
/// App.svelte
|
||||
<script>
|
||||
import FancyInput from './FancyInput.svelte';
|
||||
|
||||
let message = $state('hello');
|
||||
</script>
|
||||
|
||||
<FancyInput bind:value={message} />
|
||||
<p>{message}</p>
|
||||
```
|
||||
|
||||
The parent component doesn't _have_ to use `bind:` — it can just pass a normal prop. Some parents don't want to listen to what their children have to say.
|
||||
|
||||
In this case, you can specify a fallback value for when no prop is passed at all:
|
||||
|
||||
```js
|
||||
/// file: FancyInput.svelte
|
||||
let { value = $bindable('fallback'), ...props } = $props();
|
||||
```
|
@ -0,0 +1,44 @@
|
||||
---
|
||||
title: $inspect
|
||||
---
|
||||
|
||||
The `$inspect` rune is roughly equivalent to `console.log`, with the exception that it will re-run whenever its argument changes. `$inspect` tracks reactive state deeply, meaning that updating something inside an object or array using fine-grained reactivity will cause it to re-fire ([demo](/#H4sIAAAAAAAACkWQ0YqDQAxFfyUMhSotdZ-tCvu431AXtGOqQ2NmmMm0LOK_r7Utfby5JzeXTOpiCIPKT5PidkSVq2_n1F7Jn3uIcEMSXHSw0evHpAjaGydVzbUQCmgbWaCETZBWMPlKj29nxBDaHj_edkAiu12JhdkYDg61JGvE_s2nR8gyuBuiJZuDJTyQ7eE-IEOzog1YD80Lb0APLfdYc5F9qnFxjiKWwbImo6_llKRQVs-2u91c_bD2OCJLkT3JZasw7KLA2XCX31qKWE6vIzNk1fKE0XbmYrBTufiI8-_8D2cUWBA_AQAA)):
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = $state(0);
|
||||
let message = $state('hello');
|
||||
|
||||
$inspect(count, message); // will console.log when `count` or `message` change
|
||||
</script>
|
||||
|
||||
<button onclick={() => count++}>Increment</button>
|
||||
<input bind:value={message} />
|
||||
```
|
||||
|
||||
## $inspect(...).with
|
||||
|
||||
`$inspect` returns a property `with`, which you can invoke with a callback, which will then be invoked instead of `console.log`. The first argument to the callback is either `"init"` or `"update"`; subsequent arguments are the values passed to `$inspect` ([demo](/#H4sIAAAAAAAACkVQ24qDMBD9lSEUqlTqPlsj7ON-w7pQG8c2VCchmVSK-O-bKMs-DefKYRYx6BG9qL4XQd2EohKf1opC8Nsm4F84MkbsTXAqMbVXTltuWmp5RAZlAjFIOHjuGLOP_BKVqB00eYuKs82Qn2fNjyxLtcWeyUE2sCRry3qATQIpJRyD7WPVMf9TW-7xFu53dBcoSzAOrsqQNyOe2XUKr0Xi5kcMvdDB2wSYO-I9vKazplV1-T-d6ltgNgSG1KjVUy7ZtmdbdjqtzRcphxMS1-XubOITJtPrQWMvKnYB15_1F7KKadA_AQAA)):
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = $state(0);
|
||||
|
||||
$inspect(count).with((type, count) => {
|
||||
if (type === 'update') {
|
||||
debugger; // or `console.trace`, or whatever you want
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<button onclick={() => count++}>Increment</button>
|
||||
```
|
||||
|
||||
A convenient way to find the origin of some change is to pass `console.trace` to `with`:
|
||||
|
||||
```js
|
||||
// @errors: 2304
|
||||
$inspect(stuff).with(console.trace);
|
||||
```
|
||||
|
||||
> [!NOTE] `$inspect` only works during development. In a production build it becomes a noop.
|
@ -0,0 +1,37 @@
|
||||
---
|
||||
title: $host
|
||||
---
|
||||
|
||||
When compiling a component as a custom element, the `$host` rune provides access to the host element, allowing you to (for example) dispatch custom events ([demo](/playground/untitled#H4sIAAAAAAAAE41Ry2rDMBD8FSECtqkTt1fHFpSSL-ix7sFRNkTEXglrnTYY_3uRlDgxTaEHIfYxs7szA9-rBizPPwZOZwM89wmecqxbF70as7InaMjltrWFR3mpkQDJ8pwXVnbKkKiwItUa3RGLVtk7gTHQXRDR2lXda4CY1D0SK9nCUk0QPyfrCovsRoNFe17aQOAwGncgO2gBqRzihJXiQrEs2csYOhQ-7HgKHaLIbpRhhBG-I2eD_8ciM4KnnOCbeE5dD2P6h0Dz0-Yi_arNhPLJXBtSGi2TvSXdbpqwdsXvjuYsC1veabvvUTog2ylrapKH2G2XsMFLS4uDthQnq2t1cwKkGOGLvYU5PvaQxLsxOkPmsm97Io1Mo2yUPF6VnOZFkw1RMoopKLKAE_9gmGxyDFMwMcwN-Bx_ABXQWmOtAgAA)):
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```svelte
|
||||
/// file: Stepper.svelte
|
||||
<svelte:options customElement="my-stepper" />
|
||||
|
||||
<script>
|
||||
function dispatch(type) {
|
||||
+++$host()+++.dispatchEvent(new CustomEvent(type));
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => dispatch('decrement')}>decrement</button>
|
||||
<button onclick={() => dispatch('increment')}>increment</button>
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```svelte
|
||||
/// file: App.svelte
|
||||
<script>
|
||||
import './Stepper.svelte';
|
||||
|
||||
let count = $state(0);
|
||||
</script>
|
||||
|
||||
<my-stepper
|
||||
ondecrement={() => count -= 1}
|
||||
onincrement={() => count += 1}
|
||||
></my-stepper>
|
||||
|
||||
<p>count: {count}</p>
|
||||
```
|
@ -1,201 +0,0 @@
|
||||
---
|
||||
title: State
|
||||
---
|
||||
|
||||
Svelte 5 uses _runes_, a powerful set of primitives for controlling reactivity inside your Svelte components and inside `.svelte.js` and `.svelte.ts` modules.
|
||||
|
||||
Runes are function-like symbols that provide instructions to the Svelte compiler. You don't need to import them from anywhere — when you use Svelte, they're part of the language. This page describes the runes that are concerned with managing state in your application.
|
||||
|
||||
## `$state`
|
||||
|
||||
The `$state` rune is the at the heart of the runes API. It is used to declare reactive state:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = $state(0);
|
||||
</script>
|
||||
|
||||
<button onclick={() => count++}>
|
||||
clicks: {count}
|
||||
</button>
|
||||
```
|
||||
|
||||
Variables declared with `$state` are the variable _itself_, in other words there's no wrapper around the value that it contains. This is possible thanks to the compiler-nature of Svelte. As such, updating state is done through simple reassignment.
|
||||
|
||||
You can also use `$state` in class fields (whether public or private):
|
||||
|
||||
```js
|
||||
// @errors: 7006 2554
|
||||
class Todo {
|
||||
done = $state(false);
|
||||
text = $state();
|
||||
|
||||
constructor(text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.text = '';
|
||||
this.done = false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE] In this example, the compiler transforms `done` and `text` into `get`/`set` methods on the class prototype referencing private fields
|
||||
|
||||
Objects and arrays are made deeply reactive by wrapping them with [`Proxies`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). What that means is that in the following example, we can mutate the `entries` object and the UI will still update - but only the list item that is actually changed will rerender:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let entries = $state([
|
||||
{ id: 1, text: 'foo' },
|
||||
{ id: 2, text: 'bar' }
|
||||
]);
|
||||
</script>
|
||||
|
||||
{#each entries as entry (entry.id)}
|
||||
{entry.text}
|
||||
{/each}
|
||||
|
||||
<button onclick={() => (entries[1].text = 'baz')}>change second entry text</button>
|
||||
```
|
||||
|
||||
> [!NOTE] Only POJOs (plain old JavaScript objects) are made deeply reactive. Reactivity will stop at class boundaries and leave those alone
|
||||
|
||||
## `$state.raw`
|
||||
|
||||
State declared with `$state.raw` cannot be mutated; it can only be _reassigned_. In other words, rather than assigning to a property of an object, or using an array method like `push`, replace the object or array altogether if you'd like to update it:
|
||||
|
||||
```js
|
||||
let person = $state.raw({
|
||||
name: 'Heraclitus',
|
||||
age: 49
|
||||
});
|
||||
|
||||
// this will have no effect (and will throw an error in dev)
|
||||
person.age += 1;
|
||||
|
||||
// this will work, because we're creating a new person
|
||||
person = {
|
||||
name: 'Heraclitus',
|
||||
age: 50
|
||||
};
|
||||
```
|
||||
|
||||
This can improve performance with large arrays and objects that you weren't planning to mutate anyway, since it avoids the cost of making them reactive. Note that raw state can _contain_ reactive state (for example, a raw array of reactive objects).
|
||||
|
||||
## `$state.snapshot`
|
||||
|
||||
To take a static snapshot of a deeply reactive `$state` proxy, use `$state.snapshot`:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let counter = $state({ count: 0 });
|
||||
|
||||
function onclick() {
|
||||
// Will log `{ count: ... }` rather than `Proxy { ... }`
|
||||
console.log($state.snapshot(counter));
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
This is handy when you want to pass some state to an external library or API that doesn't expect a proxy, such as `structuredClone`.
|
||||
|
||||
## `$derived`
|
||||
|
||||
Derived state is declared with the `$derived` rune:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = $state(0);
|
||||
let doubled = $derived(count * 2);
|
||||
</script>
|
||||
|
||||
<button onclick={() => count++}>
|
||||
{doubled}
|
||||
</button>
|
||||
|
||||
<p>{count} doubled is {doubled}</p>
|
||||
```
|
||||
|
||||
The expression inside `$derived(...)` should be free of side-effects. Svelte will disallow state changes (e.g. `count++`) inside derived expressions.
|
||||
|
||||
As with `$state`, you can mark class fields as `$derived`.
|
||||
|
||||
## `$derived.by`
|
||||
|
||||
Sometimes you need to create complex derivations that don't fit inside a short expression. In these cases, you can use `$derived.by` which accepts a function as its argument.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let numbers = $state([1, 2, 3]);
|
||||
let total = $derived.by(() => {
|
||||
let total = 0;
|
||||
for (const n of numbers) {
|
||||
total += n;
|
||||
}
|
||||
return total;
|
||||
});
|
||||
</script>
|
||||
|
||||
<button onclick={() => numbers.push(numbers.length + 1)}>
|
||||
{numbers.join(' + ')} = {total}
|
||||
</button>
|
||||
```
|
||||
|
||||
In essence, `$derived(expression)` is equivalent to `$derived.by(() => expression)`.
|
||||
|
||||
## Universal reactivity
|
||||
|
||||
In the examples above, `$state` and `$derived` only appear at the top level of components. You can also use them within functions or even outside Svelte components inside `.svelte.js` or `.svelte.ts` modules.
|
||||
|
||||
```ts
|
||||
/// file: counter.svelte.ts
|
||||
export function createCounter(initial: number) {
|
||||
let count = $state(initial);
|
||||
let double = $derived(count * 2);
|
||||
return {
|
||||
get count() {
|
||||
return count;
|
||||
},
|
||||
get double() {
|
||||
return double;
|
||||
},
|
||||
increment: () => count++
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
import { createCounter } from './counter.svelte';
|
||||
|
||||
const counter = createCounter();
|
||||
</script>
|
||||
|
||||
<button onclick={counter.increment}>{counter.count} / {counter.double}</button>
|
||||
```
|
||||
|
||||
There are a few things to note in the above example:
|
||||
|
||||
- We're using getters to transport reactivity across the function boundary. This way we keep reactivity "alive". If we were to return the value itself, it would be fixed to the value at that point in time. This is no different to how regular JavaScript variables behave.
|
||||
- We're not destructuring the counter at the usage site. Because we're using getters, destructuring would fix `count` and `double` to the value at that point in time. To keep the getters "alive", we're not using destructuring. Again, this is how regular JavaScript works.
|
||||
|
||||
If you have shared state you want to manipulate from various places, you don't need to resort to getters. Instead, you can take advantage of `$state` being deeply reactive and only update its properties, not the value itself:
|
||||
|
||||
```ts
|
||||
/// file: app-state.svelte.ts
|
||||
export const appState = $state({
|
||||
loggedIn: true
|
||||
});
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
import { appState } from './app-state.svelte';
|
||||
</script>
|
||||
|
||||
<button onclick={() => (appState.loggedIn = false)}>Log out</button>
|
||||
```
|
@ -0,0 +1,40 @@
|
||||
---
|
||||
title: {#if ...}
|
||||
---
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{#if expression}...{/if}
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{#if expression}...{:else if expression}...{/if}
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{#if expression}...{:else}...{/if}
|
||||
```
|
||||
|
||||
Content that is conditionally rendered can be wrapped in an if block.
|
||||
|
||||
```svelte
|
||||
{#if answer === 42}
|
||||
<p>what was the question?</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
Additional conditions can be added with `{:else if expression}`, optionally ending in an `{:else}` clause.
|
||||
|
||||
```svelte
|
||||
{#if porridge.temperature > 100}
|
||||
<p>too hot!</p>
|
||||
{:else if 80 > porridge.temperature}
|
||||
<p>too cold!</p>
|
||||
{:else}
|
||||
<p>just right!</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
(Blocks don't have to wrap elements, they can also wrap text within elements.)
|
@ -0,0 +1,92 @@
|
||||
---
|
||||
title: {#each ...}
|
||||
---
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{#each expression as name}...{/each}
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{#each expression as name, index}...{/each}
|
||||
```
|
||||
|
||||
Iterating over values can be done with an each block. The values in question can be arrays, array-like objects (i.e. anything with a `length` property), or iterables like `Map` and `Set` — in other words, anything that can be used with `Array.from`.
|
||||
|
||||
```svelte
|
||||
<h1>Shopping list</h1>
|
||||
<ul>
|
||||
{#each items as item}
|
||||
<li>{item.name} x {item.qty}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
```
|
||||
|
||||
You can use each blocks to iterate over any array or array-like value — that is, any object with a `length` property.
|
||||
|
||||
An each block can also specify an _index_, equivalent to the second argument in an `array.map(...)` callback:
|
||||
|
||||
```svelte
|
||||
{#each items as item, i}
|
||||
<li>{i + 1}: {item.name} x {item.qty}</li>
|
||||
{/each}
|
||||
```
|
||||
|
||||
## Keyed each blocks
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{#each expression as name (key)}...{/each}
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{#each expression as name, index (key)}...{/each}
|
||||
```
|
||||
|
||||
If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to diff the list when data changes, rather than adding or removing items at the end. The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change.
|
||||
|
||||
```svelte
|
||||
{#each items as item (item.id)}
|
||||
<li>{item.name} x {item.qty}</li>
|
||||
{/each}
|
||||
|
||||
<!-- or with additional index value -->
|
||||
{#each items as item, i (item.id)}
|
||||
<li>{i + 1}: {item.name} x {item.qty}</li>
|
||||
{/each}
|
||||
```
|
||||
|
||||
You can freely use destructuring and rest patterns in each blocks.
|
||||
|
||||
```svelte
|
||||
{#each items as { id, name, qty }, i (id)}
|
||||
<li>{i + 1}: {name} x {qty}</li>
|
||||
{/each}
|
||||
|
||||
{#each objects as { id, ...rest }}
|
||||
<li><span>{id}</span><MyComponent {...rest} /></li>
|
||||
{/each}
|
||||
|
||||
{#each items as [id, ...rest]}
|
||||
<li><span>{id}</span><MyComponent values={rest} /></li>
|
||||
{/each}
|
||||
```
|
||||
|
||||
## Else blocks
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{#each expression as name}...{:else}...{/each}
|
||||
```
|
||||
|
||||
An each block can also have an `{:else}` clause, which is rendered if the list is empty.
|
||||
|
||||
```svelte
|
||||
{#each todos as todo}
|
||||
<p>{todo.text}</p>
|
||||
{:else}
|
||||
<p>No tasks today!</p>
|
||||
{/each}
|
||||
```
|
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: {#key ...}
|
||||
---
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{#key expression}...{/key}
|
||||
```
|
||||
|
||||
Key blocks destroy and recreate their contents when the value of an expression changes. When used around components, this will cause them to be reinstantiated and reinitialised:
|
||||
|
||||
```svelte
|
||||
{#key value}
|
||||
<Component />
|
||||
{/key}
|
||||
```
|
||||
|
||||
It's also useful if you want a transition to play whenever a value changes:
|
||||
|
||||
```svelte
|
||||
{#key value}
|
||||
<div transition:fade>{value}</div>
|
||||
{/key}
|
||||
```
|
@ -0,0 +1,39 @@
|
||||
---
|
||||
title: {@render ...}
|
||||
---
|
||||
|
||||
To render a [snippet](snippet), use a `{@render ...}` tag.
|
||||
|
||||
```svelte
|
||||
{#snippet sum(a, b)}
|
||||
<p>{a} + {b} = {a + b}</p>
|
||||
{/snippet}
|
||||
|
||||
{@render sum(1, 2)}
|
||||
{@render sum(3, 4)}
|
||||
{@render sum(5, 6)}
|
||||
```
|
||||
|
||||
The expression can be an identifer like `sum`, or an arbitrary JavaScript expression:
|
||||
|
||||
```svelte
|
||||
{@render (cool ? coolSnippet : lameSnippet)()}
|
||||
```
|
||||
|
||||
## Optional snippets
|
||||
|
||||
If the snippet is potentially undefined — for example, because it's an incoming prop — then you can use optional chaining to only render it when it _is_ defined:
|
||||
|
||||
```svelte
|
||||
{@render children?.()}
|
||||
```
|
||||
|
||||
Alternatively, use an [`{#if ...}`](if) block with an `:else` clause to render fallback content:
|
||||
|
||||
```svelte
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else}
|
||||
<p>fallback content</p>
|
||||
{/if}
|
||||
```
|
@ -0,0 +1,51 @@
|
||||
---
|
||||
title: {@html ...}
|
||||
---
|
||||
|
||||
To inject raw HTML into your component, use the `{@html ...}` tag:
|
||||
|
||||
```svelte
|
||||
<article>
|
||||
{@html content}
|
||||
</article>
|
||||
```
|
||||
|
||||
> [!NOTE] Make sure that you either escape the passed string or only populate it with values that are under your control in order to prevent [XSS attacks](https://owasp.org/www-community/attacks/xss/). Never render unsanitized content.
|
||||
|
||||
The expression should be valid standalone HTML — this will not work, because `</div>` is not valid HTML:
|
||||
|
||||
```svelte
|
||||
{@html '<div>'}content{@html '</div>'}
|
||||
```
|
||||
|
||||
It also will not compile Svelte code.
|
||||
|
||||
## Styling
|
||||
|
||||
Content rendered this way is 'invisible' to Svelte and as such will not receive [scoped styles](styles-and-classes) — in other words, this will not work, and the `a` and `img` styles will be regarded as unused:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```svelte
|
||||
<article>
|
||||
{@html content}
|
||||
</article>
|
||||
|
||||
<style>
|
||||
article {
|
||||
a { color: hotpink }
|
||||
img { width: 100% }
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
Instead, use the `:global` modifier to target everything inside the `<article>`:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```svelte
|
||||
<style>
|
||||
article +++:global+++ {
|
||||
a { color: hotpink }
|
||||
img { width: 100% }
|
||||
}
|
||||
</style>
|
||||
```
|
@ -0,0 +1,14 @@
|
||||
---
|
||||
title: {@const ...}
|
||||
---
|
||||
|
||||
The `{@const ...}` tag defines a local constant.
|
||||
|
||||
```svelte
|
||||
{#each boxes as box}
|
||||
{@const area = box.width * box.height}
|
||||
{box.width} * {box.height} = {area}
|
||||
{/each}
|
||||
```
|
||||
|
||||
`{@const}` is only allowed as an immediate child of a block — `{#if ...}`, `{#each ...}`, `{#snippet ...}` and so on — or a `<Component />`.
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Actions
|
||||
title: use:
|
||||
---
|
||||
|
||||
- template syntax
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
title: in: and out:
|
||||
---
|
||||
|
||||
Coming soon!
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
title: animate:
|
||||
---
|
||||
|
||||
Coming soon!
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
title: class: and style:
|
||||
---
|
||||
|
||||
Coming soon!
|
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: Data fetching
|
||||
---
|
||||
|
||||
Fetching data is a fundamental part of apps interacting with the outside world. Svelte is unopinionated with how you fetch your data. The simplest way would be using the built-in `fetch` method:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let response = $state();
|
||||
fetch('/api/data').then(async (r) => (response = r.json()));
|
||||
</script>
|
||||
```
|
||||
|
||||
While this works, it makes working with promises somewhat unergonomic. Svelte alleviates this problem using the `#await` block.
|
||||
|
||||
## {#await ...}
|
||||
|
||||
## SvelteKit loaders
|
||||
|
||||
Fetching inside your components is great for simple use cases, but it's prone to data loading waterfalls and makes code harder to work with because of the promise handling. SvelteKit solves this problem by providing a opinionated data loading story that is coupled to its router. Learn more about it [in the docs](../kit).
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Styling
|
||||
---
|
@ -1,94 +0,0 @@
|
||||
---
|
||||
title: Debugging
|
||||
---
|
||||
|
||||
- `@debug`
|
||||
- `$inspect`
|
||||
|
||||
Svelte provides two built-in ways to debug your application.
|
||||
|
||||
## `$inspect`
|
||||
|
||||
The `$inspect` rune is roughly equivalent to `console.log`, with the exception that it will re-run whenever its
|
||||
argument changes. `$inspect` tracks reactive state deeply, meaning that updating something inside an object
|
||||
or array using fine-grained reactivity will cause it to re-fire. ([Demo:](/#H4sIAAAAAAAACkWQ0YqDQAxFfyUMhSotdZ-tCvu431AXtGOqQ2NmmMm0LOK_r7Utfby5JzeXTOpiCIPKT5PidkSVq2_n1F7Jn3uIcEMSXHSw0evHpAjaGydVzbUQCmgbWaCETZBWMPlKj29nxBDaHj_edkAiu12JhdkYDg61JGvE_s2nR8gyuBuiJZuDJTyQ7eE-IEOzog1YD80Lb0APLfdYc5F9qnFxjiKWwbImo6_llKRQVs-2u91c_bD2OCJLkT3JZasw7KLA2XCX31qKWE6vIzNk1fKE0XbmYrBTufiI8-_8D2cUWBA_AQAA))
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = $state(0);
|
||||
let message = $state('hello');
|
||||
|
||||
$inspect(count, message); // will console.log when `count` or `message` change
|
||||
</script>
|
||||
|
||||
<button onclick={() => count++}>Increment</button>
|
||||
<input bind:value={message} />
|
||||
```
|
||||
|
||||
`$inspect` returns a property `with`, which you can invoke with a callback, which will then be invoked instead of `console.log`. The first argument to the callback is either `"init"` or `"update"`, all following arguments are the values passed to `$inspect`. [Demo:](/#H4sIAAAAAAAACkVQ24qDMBD9lSEUqlTqPlsj7ON-w7pQG8c2VCchmVSK-O-bKMs-DefKYRYx6BG9qL4XQd2EohKf1opC8Nsm4F84MkbsTXAqMbVXTltuWmp5RAZlAjFIOHjuGLOP_BKVqB00eYuKs82Qn2fNjyxLtcWeyUE2sCRry3qATQIpJRyD7WPVMf9TW-7xFu53dBcoSzAOrsqQNyOe2XUKr0Xi5kcMvdDB2wSYO-I9vKazplV1-T-d6ltgNgSG1KjVUy7ZtmdbdjqtzRcphxMS1-XubOITJtPrQWMvKnYB15_1F7KKadA_AQAA)
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = $state(0);
|
||||
|
||||
$inspect(count).with((type, count) => {
|
||||
if (type === 'update') {
|
||||
debugger; // or `console.trace`, or whatever you want
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<button onclick={() => count++}>Increment</button>
|
||||
```
|
||||
|
||||
A convenient way to find the origin of some change is to pass `console.trace` to `with`:
|
||||
|
||||
```js
|
||||
// @errors: 2304
|
||||
$inspect(stuff).with(console.trace);
|
||||
```
|
||||
|
||||
> [!NOTE] `$inspect` only works during development. In a production build it becomes a noop.
|
||||
|
||||
## @debug
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{@debug}
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{@debug var1, var2, ..., varN}
|
||||
```
|
||||
|
||||
The `{@debug ...}` tag offers an alternative to `console.log(...)`. It logs the values of specific variables whenever they change, and pauses code execution if you have devtools open.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let user = {
|
||||
firstname: 'Ada',
|
||||
lastname: 'Lovelace'
|
||||
};
|
||||
</script>
|
||||
|
||||
{@debug user}
|
||||
|
||||
<h1>Hello {user.firstname}!</h1>
|
||||
```
|
||||
|
||||
`{@debug ...}` accepts a comma-separated list of variable names (not arbitrary expressions).
|
||||
|
||||
```svelte
|
||||
<!-- Compiles -->
|
||||
{@debug user}
|
||||
{@debug user1, user2, user3}
|
||||
|
||||
<!-- WON'T compile -->
|
||||
{@debug user.firstname}
|
||||
{@debug myArray[0]}
|
||||
{@debug !isReady}
|
||||
{@debug typeof user === 'object'}
|
||||
```
|
||||
|
||||
The `{@debug}` tag without any arguments will insert a `debugger` statement that gets triggered when _any_ state changes, as opposed to the specified variables.
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
title: Svelte 5 migration guide
|
||||
---
|
||||
|
||||
- the stuff from the preview docs and possibly more
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Special elements
|
||||
---
|
@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Debugging
|
||||
---
|
||||
|
||||
- `@debug`
|
||||
- `$inspect`
|
||||
|
||||
Svelte provides two built-in ways to debug your application.
|
||||
|
||||
## `$inspect`
|
||||
|
||||
TODO
|
||||
|
||||
## @debug
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{@debug}
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- copy: false --->
|
||||
{@debug var1, var2, ..., varN}
|
||||
```
|
||||
|
||||
The `{@debug ...}` tag offers an alternative to `console.log(...)`. It logs the values of specific variables whenever they change, and pauses code execution if you have devtools open.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let user = {
|
||||
firstname: 'Ada',
|
||||
lastname: 'Lovelace'
|
||||
};
|
||||
</script>
|
||||
|
||||
{@debug user}
|
||||
|
||||
<h1>Hello {user.firstname}!</h1>
|
||||
```
|
||||
|
||||
`{@debug ...}` accepts a comma-separated list of variable names (not arbitrary expressions).
|
||||
|
||||
```svelte
|
||||
<!-- Compiles -->
|
||||
{@debug user}
|
||||
{@debug user1, user2, user3}
|
||||
|
||||
<!-- WON'T compile -->
|
||||
{@debug user.firstname}
|
||||
{@debug myArray[0]}
|
||||
{@debug !isReady}
|
||||
{@debug typeof user === 'object'}
|
||||
```
|
||||
|
||||
The `{@debug}` tag without any arguments will insert a `debugger` statement that gets triggered when _any_ state changes, as opposed to the specified variables.
|
@ -0,0 +1,920 @@
|
||||
---
|
||||
title: Svelte 5 migration guide
|
||||
---
|
||||
|
||||
Version 5 comes with an overhauled syntax and reactivity system. While it may look different at first, you'll soon notice many similarities. This guide goes over the changes in detail and shows you how to upgrade. Along with it, we also provide information on _why_ we did these changes.
|
||||
|
||||
You don't have to migrate to the new syntax right away - Svelte 5 still supports the old Svelte 4 syntax, and you can mix and match components using the new syntax with components using the old and vice versa. We expect many people to be able to upgrade with only a few lines of code changed initially. There's also a [migration script](#Migration-script) that helps you with many of these steps automatically.
|
||||
|
||||
## Reactivity syntax changes
|
||||
|
||||
At the heart of Svelte 5 is the new runes API. Runes are basically compiler instructions that inform Svelte about reactivity. Syntactically, runes are functions starting with a dollar-sign.
|
||||
|
||||
### let -> $state
|
||||
|
||||
In Svelte 4, a `let` declaration at the top level of a component was implicitly reactive. In Svelte 5, things are more explicit: a variable is reactive when created using the `$state` rune. Let's migrate the counter to runes mode by wrapping the counter in `$state`:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = +++$state(+++0+++)+++;
|
||||
</script>
|
||||
```
|
||||
|
||||
Nothing else changes. `count` is still the number itself, and you read and write directly to it, without a wrapper like `.value` or `getCount()`.
|
||||
|
||||
> [!DETAILS] Why we did this
|
||||
> `let` being implicitly reactive at the top level worked great, but it meant that reactivity was constrained - a `let` declaration anywhere else was not reactive. This forced you to resort to using stores when refactoring code out of the top level of components for reuse. This meant you had to learn an entirely separate reactivity model, and the result often wasn't as nice to work with. Because reactivity is more explicit in Svelte 5, you can keep using the same API in an outside the top level of components. Head to TODO LINK TO TUTORIAL to learn more.
|
||||
|
||||
### $: -> $derived/$effect
|
||||
|
||||
In Svelte 4, a `$:` statement at the top level of a component could be used to declare a derivation, i.e. state that is entirely defined through a computation of other state. In Svelte 5, this is achieved using the `$derived` rune:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = +++$state(+++0+++)+++;
|
||||
---$:--- +++const+++ double = +++$derived(+++count * 2+++)+++;
|
||||
</script>
|
||||
```
|
||||
|
||||
As with `$state`, nothing else changes. `double` is still the number itself, and you read it directly, without a wrapper like `.value` or `getCount()`.
|
||||
|
||||
A `$:` statement could also be used to create side effects. In Svelte 5, this is achieved using the `$effect` rune:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = +++$state(+++0+++)+++;
|
||||
---$:---+++$effect(() =>+++ {
|
||||
if (count > 5) {
|
||||
alert('Count is too high!');
|
||||
}
|
||||
}+++);+++
|
||||
</script>
|
||||
```
|
||||
|
||||
> [!DETAILS] Why we did this
|
||||
> `$:` was a great shorthand and easy to get started with: you could slap a `$:` in front of most code and it would somehow work. This intuitiveness was also its drawback the more complicated your code became, because it wasn't as easy to reason about. Was the intent of the code to create a derivation, or a side effect? With `$derived` and `$effect`, you have a bit more up-front decision making to do (spoiler alert: 90% of the time you want `$derived`), but future-you and other developers on your team will have an easier time.
|
||||
>
|
||||
> There were also gotchas that were hard to spot:
|
||||
>
|
||||
> - `$:` only updated directly before rendering, which meant you could read stale values in-between rerenders
|
||||
> - `$:` only ran once per tick, which meant that statements may run less often than you think
|
||||
> - `$:` dependencies were determined through static analysis of the dependencies. This worked in most cases, but could break in subtle ways during a refactoring where dependencies would be > for example moved into a function and no longer be visible as a result
|
||||
> - `$:` statements were also ordered by using static analysis of the dependencies. In some cases there could be ties and the ordering would be wrong as a result, needing manual > interventions. Ordering could also break while refactoring code and some dependencies no longer being visible as a result.
|
||||
>
|
||||
> Lastly, it wasn't TypeScript-friendly (our editor tooling had to jump through some hoops to make it valid for TypeScript), which was a blocker for making Svelte's reactivity model truly > universal.
|
||||
>
|
||||
> `$derived` and `$effect` fix all of these by
|
||||
>
|
||||
> - always returning the latest value
|
||||
> - running as often as needed to be stable
|
||||
> - determining the dependencies at runtime, and therefore being immune to refactorings
|
||||
> - executing dependencies as needed and therefore being immune to ordering problems
|
||||
> - being TypeScript-friendly
|
||||
|
||||
### export let -> $props
|
||||
|
||||
In Svelte 4, properties of a component were declared using `export let`. Each property was one declaration. In Svelte 5, all properties are declared through the `$props` rune, through destructuring:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
---export let optional = 'unset';
|
||||
export let required;---
|
||||
+++let { optional = 'unset', required } = $props();+++
|
||||
</script>
|
||||
```
|
||||
|
||||
There are multiple cases where declaring properties becomes less straightforward than having a few `export let` declarations:
|
||||
|
||||
- you want to rename the property, for example because the name is a reserved identifier (e.g. `class`)
|
||||
- you don't know which other properties to expect in advance
|
||||
- you want to forward every property to another component
|
||||
|
||||
All these cases need special syntax in Svelte 4:
|
||||
|
||||
- renaming: `export { klass as class}`
|
||||
- other properties: `$$restProps`
|
||||
- all properties `$$props`
|
||||
|
||||
In Svelte 5, the `$props` rune makes this straightforward without any additional Svelte-specific syntax:
|
||||
|
||||
- renaming: use property renaming `let { class: klass } = $props();`
|
||||
- other properties: use spreading `let { foo, bar, ...rest } = $props();`
|
||||
- all properties: don't destructure `let props = $props();`
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
---let klass = '';
|
||||
export { klass as class};---
|
||||
+++let { class: klass, ...rest } = $props();+++
|
||||
</script>
|
||||
<button {class} {...---$$restProps---+++rest+++}>click me</button>
|
||||
```
|
||||
|
||||
> [!DETAILS] Why we did this
|
||||
> `export let` was one of the more controversial API decisions, and there was a lot of debate about whether you should think about a property being `export`ed or `import`ed. `$props` doesn't have this trait. It's also in line with the other runes, and the general thinking reduces to "everything special to reactivity in Svelte is a rune".
|
||||
>
|
||||
> There were also a lot of limitations around `export let`, which required additional API, as shown above. `$props` unite this in one syntactical concept that leans heavily on regular JavaScript destructuring syntax.
|
||||
|
||||
## Event changes
|
||||
|
||||
Event handlers have been given a facelift in Svelte 5. Whereas in Svelte 4 we use the `on:` directive to attach an event listener to an element, in Svelte 5 they are properties like any other (in other words - remove the colon):
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = $state(0);
|
||||
</script>
|
||||
|
||||
<button on---:---click={() => count++}>
|
||||
clicks: {count}
|
||||
</button>
|
||||
```
|
||||
|
||||
Since they're just properties, you can use the normal shorthand syntax...
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = $state(0);
|
||||
|
||||
function onclick() {
|
||||
count++;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button {onclick}>
|
||||
clicks: {count}
|
||||
</button>
|
||||
```
|
||||
|
||||
...though when using a named event handler function it's usually better to use a more descriptive name.
|
||||
|
||||
### Component events
|
||||
|
||||
In Svelte 4, components could emit events by creating a dispatcher with `createEventDispatcher`.
|
||||
|
||||
This function is deprecated in Svelte 5. Instead, components should accept _callback props_ - which means you then pass functions as properties to these components:
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
import Pump from './Pump.svelte';
|
||||
|
||||
let size = $state(15);
|
||||
let burst = $state(false);
|
||||
|
||||
function reset() {
|
||||
size = 15;
|
||||
burst = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Pump
|
||||
---on:---inflate={(power) => {
|
||||
size += power---.details---;
|
||||
if (size > 75) burst = true;
|
||||
}}
|
||||
---on:---deflate={(power) => {
|
||||
if (size > 0) size -= power---.details---;
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if burst}
|
||||
<button onclick={reset}>new balloon</button>
|
||||
<span class="boom">💥</span>
|
||||
{:else}
|
||||
<span class="balloon" style="scale: {0.01 * size}">
|
||||
🎈
|
||||
</span>
|
||||
{/if}
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- file: Pump.svelte --->
|
||||
<script>
|
||||
---import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
---
|
||||
+++let { inflate, deflate } = $props();+++
|
||||
let power = $state(5);
|
||||
</script>
|
||||
|
||||
<button onclick={() => ---dispatch('inflate', power)---+++inflate(power)+++}>
|
||||
inflate
|
||||
</button>
|
||||
<button onclick={() => ---dispatch('deflate', power)---+++deflate(power)+++}>
|
||||
deflate
|
||||
</button>
|
||||
<button onclick={() => power--}>-</button>
|
||||
Pump power: {power}
|
||||
<button onclick={() => power++}>+</button>
|
||||
```
|
||||
|
||||
### Bubbling events
|
||||
|
||||
Instead of doing `<button on:click>` to 'forward' the event from the element to the component, the component should accept an `onclick` callback prop:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
+++let { onclick } = $props();+++
|
||||
</script>
|
||||
|
||||
<button ---on:click--- +++{onclick}+++>
|
||||
click me
|
||||
</button>
|
||||
```
|
||||
|
||||
Note that this also means you can 'spread' event handlers onto the element along with other props instead of tediously forwarding each event separately:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let props = $props();
|
||||
</script>
|
||||
|
||||
<button ---{...$$props} on:click on:keydown on:all_the_other_stuff--- +++{...props}+++>
|
||||
click me
|
||||
</button>
|
||||
```
|
||||
|
||||
### Event modifiers
|
||||
|
||||
In Svelte 4, you can add event modifiers to handlers:
|
||||
|
||||
```svelte
|
||||
<button on:click|once|preventDefault={handler}>...</button>
|
||||
```
|
||||
|
||||
Modifiers are specific to `on:` and as such do not work with modern event handlers. Adding things like `event.preventDefault()` inside the handler itself is preferable, since all the logic lives in one place rather than being split between handler and modifiers.
|
||||
|
||||
Since event handlers are just functions, you can create your own wrappers as necessary:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
function once(fn) {
|
||||
return function (event) {
|
||||
if (fn) fn.call(this, event);
|
||||
fn = null;
|
||||
};
|
||||
}
|
||||
|
||||
function preventDefault(fn) {
|
||||
return function (event) {
|
||||
event.preventDefault();
|
||||
fn.call(this, event);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={once(preventDefault(handler))}>...</button>
|
||||
```
|
||||
|
||||
There are three modifiers — `capture`, `passive` and `nonpassive` — that can't be expressed as wrapper functions, since they need to be applied when the event handler is bound rather than when it runs.
|
||||
|
||||
For `capture`, we add the modifier to the event name:
|
||||
|
||||
```svelte
|
||||
<button onclickcapture={...}>...</button>
|
||||
```
|
||||
|
||||
Changing the [`passive`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#using_passive_listeners) option of an event handler, meanwhile, is not something to be done lightly. If you have a use case for it — and you probably don't! — then you will need to use an action to apply the event handler yourself.
|
||||
|
||||
### Multiple event handlers
|
||||
|
||||
In Svelte 4, this is possible:
|
||||
|
||||
```svelte
|
||||
<button on:click={one} on:click={two}>...</button>
|
||||
```
|
||||
|
||||
Duplicate attributes/properties on elements — which now includes event handlers — are not allowed. Instead, do this:
|
||||
|
||||
```svelte
|
||||
<button
|
||||
onclick={(e) => {
|
||||
one(e);
|
||||
two(e);
|
||||
}}
|
||||
>
|
||||
...
|
||||
</button>
|
||||
```
|
||||
|
||||
When spreading props, local event handlers must go _after_ the spread, or they risk being overwritten:
|
||||
|
||||
```svelte
|
||||
<button
|
||||
{...props}
|
||||
onclick={(e) => {
|
||||
doStuff(e);
|
||||
props.onclick?.(e);
|
||||
}}
|
||||
>
|
||||
...
|
||||
</button>
|
||||
```
|
||||
|
||||
> [!DETAILS] Why we did this
|
||||
> `createEventDispatcher` was always a bit boilerplate-y:
|
||||
>
|
||||
> - import the function
|
||||
> - call the function to get a dispatch function
|
||||
> - call said dispatch function with a string and possibly a payload
|
||||
> - retrieve said payload on the other end through a `.details` property, because the event itself was always a `CustomEvent`
|
||||
>
|
||||
> It was always possible to use component callback props, but because you had to listen to dom events using `on:`, it made sense to use `createEventDispatcher` for component events due to > syntactical consiscency. Now that we have event attributes (`onclick`), it's the other way around: Callback props are now the more sensible thing to do.
|
||||
>
|
||||
> The removal of event modifiers is arguably one of the changes that seems like a step back for those who've liked the shorthand syntax of event modifiers. Given that they are not used that > frequently, we traded a smaller surface area for more explicitness. Modifiers also were inconsistent, because most of them were only useable on Dom elements.
|
||||
>
|
||||
> Multiple listeners for the same event are also no longer possible, but it was something of an anti-pattern anyway, since it impedes readability: if there are many attributes, it becomes > harder to spot that there are two handlers unless they are right next to each other. It also implies that the two handlers are independent, when in fact something like `event.> stopImmediatePropagation()` inside `one` would prevent `two` from being called.
|
||||
>
|
||||
> By deprecating `createEventDispatcher` and the `on:` directive in favour of callback props and normal element properties, we:
|
||||
>
|
||||
> - reduce Svelte's learning curve
|
||||
> - remove boilerplate, particularly around `createEventDispatcher`
|
||||
> - remove the overhead of creating `CustomEvent` objects for events that may not even have listeners
|
||||
> - add the ability to spread event handlers
|
||||
> - add the ability to know which event handlers were provided to a component
|
||||
> - add the ability to express whether a given event handler is required or optional
|
||||
> - increase type safety (previously, it was effectively impossible for Svelte to guarantee that a component didn't emit a particular event)
|
||||
|
||||
## Snippets instead of slots
|
||||
|
||||
In Svelte 4, content can be passed to components using slots. Svelte 5 replaces them with snippets which are more powerful and flexible, and as such slots are deprecated in Svelte 5.
|
||||
|
||||
They continue to work, however, and you can mix and match snippets and slots in your components.
|
||||
|
||||
When using custom elements, you should still use `<slot />` like before. In a future version, when Svelte removes its internal version of slots, it will leave those slots as-is, i.e. output a regular DOM tag instead of transforming it.
|
||||
|
||||
### Default content
|
||||
|
||||
In Svelte 4, the easiest way to pass a piece of UI to the child was using a `<slot />`. In Svelte 5, this is done using the `children` prop instead, which is then shown with `{@render children()}`:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
+++let { children } = $props();+++
|
||||
</script>
|
||||
|
||||
---<slot />---
|
||||
+++{@render children?.()}+++
|
||||
```
|
||||
|
||||
### Multiple content placeholders
|
||||
|
||||
If you wanted multiple UI placeholders, you had to use named slots. In Svelte 5, use props instead, name them however you like and `{@render ...}` them:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
+++let { header, main, footer } = $props();+++
|
||||
</script>
|
||||
|
||||
<header>
|
||||
---<slot name="header" />---
|
||||
+++{@render header()}+++
|
||||
</header>
|
||||
|
||||
<main>
|
||||
---<slot name="main" />---
|
||||
+++{@render main()}+++
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
---<slot name="header" />---
|
||||
+++{@render footer()}+++
|
||||
</footer>
|
||||
```
|
||||
|
||||
### Passing data back up
|
||||
|
||||
In Svelte 4, you would pass data to a `<slot />` and then retrieve it with `let:` in the parent component. In Svelte 5, snippets take on that responsibility:
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
import List from './List.svelte';
|
||||
</script>
|
||||
|
||||
<List items={['one', 'two', 'three']} ---let:item--->
|
||||
+++{#snippet item(text)}+++
|
||||
<span>{text}</span>
|
||||
+++{/snippet}+++
|
||||
---<span slot="empty">No items yet</span>---
|
||||
+++{#snippet empty()}
|
||||
<span>No items yet</span>
|
||||
{/snippet}+++
|
||||
</List>
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- file: List.svelte --->
|
||||
<script>
|
||||
let { items, +++item, empty+++ } = $props();
|
||||
</script>
|
||||
|
||||
{#if items.length}
|
||||
<ul>
|
||||
{#each items as entry}
|
||||
<li>
|
||||
---<slot item={entry} />---
|
||||
+++{@render item(entry)}+++
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
---<slot name="empty" />---
|
||||
+++{@render empty?.()}+++
|
||||
{/if}
|
||||
```
|
||||
|
||||
> [!DETAILS] Why we did this
|
||||
> Slots were easy to get started with, but the more advanced the use case became, the more involved and confusing the syntax became:
|
||||
>
|
||||
> - the `let:` syntax was confusing to many people as it _creates_ a variable whereas all other `:` directives _receive_ a variable
|
||||
> - the scope of a variable declared with `let:` wasn't clear. In the example above, it may look like you can use the `item` slot prop in the `empty` slot, but that's not true
|
||||
> - named slots had to be applied to an element using the `slot` attribute. Sometimes you didn't want to create an element, so we had to add the `<svelte:fragment>` API
|
||||
> - named slots could also be applied to a component, which changed the semantics of where `let:` directives are available (even today us maintainers often don't know which way around it > works)
|
||||
>
|
||||
> Snippets solve all of these problems by being much more readable and clear. At the same time they're more powerful as they allow you to define sections of UI that you can render _anywhere_, not just passing them as props to a component.
|
||||
|
||||
## Migration script
|
||||
|
||||
By now you should have a pretty good understanding of the before/after and how the old syntax relates to the new syntax. It probably also became clear that a lot of these migrations are rather technical and repetitive - something you don't want to do by hand.
|
||||
|
||||
We thought the same, which is why we provide a migration script to do most of the migration automatically. You can upgrade your project by using `npx sv migrate svelte-5`. This will do the following things:
|
||||
|
||||
- bump core dependencies in your `package.json`
|
||||
- migrate to runes (`let` -> `$state` etc)
|
||||
- migrate to event attributes for Dom elements (`on:click` -> `onclick`)
|
||||
- migrate slot creations to render tags (`<slot />` -> `{@render children()}`)
|
||||
- migrate slot usages to snippets (`<div slot="x">...</div>` -> `{#snippet x()}<div>...</div>{/snippet}`)
|
||||
- migrate obvious component creations (`new Component(...)` -> `mount(Component, ...)`)
|
||||
|
||||
You can also migrate a single component in VS Code through the `Migrate Component to Svelte 5 Syntax` command, or in our Playground through the `Migrate` button.
|
||||
|
||||
Not everything can be migrated automatically, and some migrations need manual cleanup afterwards. The following sections describe these in more detail.
|
||||
|
||||
### run
|
||||
|
||||
You may see that the migration script converts some of your `$:` statements to a `run` function which is imported from `svelte/legacy`. This happens if the migration script couldn't reliably migrate the statement to a `$derived` and concluded this is a side effect instead. In some cases this may be wrong and it's best to change this to use a `$derived` instead. In other cases it may be right, but since `$:` statements also ran on the server but `$effect` does not, it isn't safe to transform it as such. Instead, `run` is used as a stopgap solution. `run` mimics most of the characteristics of `$:`, in that it runs on the server once, and runs as `$effect.pre` on the client (`$effect.pre` runs _before_ changes are applied to the DOM; most likely you want to use `$effect` instead).
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
---import { run } from 'svelte/legacy';---
|
||||
---run(() => {---
|
||||
+++$effect(() => {+++
|
||||
// some side effect code
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### Event modifiers
|
||||
|
||||
Event modifiers are not applicable to event attributes (e.g. you can't do `onclick|preventDefault={...}`). Therefore, when migrating event directives to event attributes, we need a function-replacement for these modifiers. These are imported from `svelte/legacy`, and should be migrated away from in favor of e.g. just using `event.preventDefault()`.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
---import { preventDefault } from 'svelte/legacy';---
|
||||
</script>
|
||||
|
||||
<button
|
||||
onclick={---preventDefault---((event) => {
|
||||
+++event.preventDefault();+++
|
||||
// ...
|
||||
})}
|
||||
>
|
||||
click me
|
||||
</button>
|
||||
```
|
||||
|
||||
### Things that are not automigrated
|
||||
|
||||
The migration script does not convert `createEventDispatcher`. You need to adjust those parts manually. It doesn't do it because it's too risky because it could result in breakage for users of the component, which the migration script cannot find out.
|
||||
|
||||
The migration script does not convert `beforeUpdate/afterUpdate`. It doesn't do it because it's impossible to determine the actual intent of the code. As a rule of thumb you can often go with a combination of `$effect.pre` (runs at the same time as `beforeUpdate` did) and `tick` (imported from `svelte`, allows you to wait until changes are applied to the DOM and then do some work).
|
||||
|
||||
## Components are no longer classes
|
||||
|
||||
In Svelte 3 and 4, components are classes. In Svelte 5 they are functions and should be instantiated differently. If you need to manually instantiate components, you should use `mount` or `hydrate` (imported from `svelte`) instead. If you see this error using SvelteKit, try updating to the latest version of SvelteKit first, which adds support for Svelte 5. If you're using Svelte without SvelteKit, you'll likely have a `main.js` file (or similar) which you need to adjust:
|
||||
|
||||
```js
|
||||
+++import { mount } from 'svelte';+++
|
||||
import App from './App.svelte'
|
||||
|
||||
---const app = new App({ target: document.getElementById("app") });---
|
||||
+++const app = mount(App, { target: document.getElementById("app") });+++
|
||||
|
||||
export default app;
|
||||
```
|
||||
|
||||
`mount` and `hydrate` have the exact same API. The difference is that `hydrate` will pick up the Svelte's server-rendered HTML inside its target and hydrate it. Both return an object with the exports of the component and potentially property accessors (if compiled with `accessors: true`). They do not come with the `$on`, `$set` and `$destroy` methods you may know from the class component API. These are its replacements:
|
||||
|
||||
For `$on`, instead of listening to events, pass them via the `events` property on the options argument.
|
||||
|
||||
```js
|
||||
+++import { mount } from 'svelte';+++
|
||||
import App from './App.svelte'
|
||||
|
||||
---const app = new App({ target: document.getElementById("app") });
|
||||
app.$on('event', callback);---
|
||||
+++const app = mount(App, { target: document.getElementById("app"), events: { event: callback } });+++
|
||||
```
|
||||
|
||||
> [!NOTE] Note that using `events` is discouraged — instead, [use callbacks](#Event-changes)
|
||||
|
||||
For `$set`, use `$state` instead to create a reactive property object and manipulate it. If you're doing this inside a `.js` or `.ts` file, adjust the ending to include `.svelte`, i.e. `.svelte.js` or `.svelte.ts`.
|
||||
|
||||
```js
|
||||
+++import { mount } from 'svelte';+++
|
||||
import App from './App.svelte'
|
||||
|
||||
---const app = new App({ target: document.getElementById("app"), props: { foo: 'bar' } });
|
||||
app.$set('event', { foo: 'baz' });---
|
||||
+++const props = $state({ foo: 'bar' });
|
||||
const app = mount(App, { target: document.getElementById("app"), props });
|
||||
props.foo = 'baz';+++
|
||||
```
|
||||
|
||||
For `$destroy`, use `unmount` instead.
|
||||
|
||||
```js
|
||||
+++import { mount, unmount } from 'svelte';+++
|
||||
import App from './App.svelte'
|
||||
|
||||
---const app = new App({ target: document.getElementById("app"), props: { foo: 'bar' } });
|
||||
app.$destroy();---
|
||||
+++const app = mount(App, { target: document.getElementById("app") });
|
||||
unmount(app);+++
|
||||
```
|
||||
|
||||
As a stop-gap-solution, you can also use `createClassComponent` or `asClassComponent` (imported from `svelte/legacy`) instead to keep the same API known from Svelte 4 after instantiating.
|
||||
|
||||
```js
|
||||
+++import { createClassComponent } from 'svelte/legacy';+++
|
||||
import App from './App.svelte'
|
||||
|
||||
---const app = new App({ target: document.getElementById("app") });---
|
||||
+++const app = createClassComponent({ component: App, target: document.getElementById("app") });+++
|
||||
|
||||
export default app;
|
||||
```
|
||||
|
||||
If this component is not under your control, you can use the `compatibility.componentApi` compiler option for auto-applied backwards compatibility, which means code using `new Component(...)` keeps working without adjustments (note that this adds a bit of overhead to each component). This will also add `$set` and `$on` methods for all component instances you get through `bind:this`.
|
||||
|
||||
```js
|
||||
/// svelte.config.js
|
||||
export default {
|
||||
compilerOptions: {
|
||||
compatibility: {
|
||||
componentApi: 4
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Note that `mount` and `hydrate` are _not_ synchronous, so things like `onMount` won't have been called by the time the function returns and the pending block of promises will not have been rendered yet (because `#await` waits a microtask to wait for a potentially immediately-resolved promise). If you need that guarantee, call `flushSync` (import from `'svelte'`) after calling `mount/hydrate`.
|
||||
|
||||
### Server API changes
|
||||
|
||||
Similarly, components no longer have a `render` method when compiled for server side rendering. Instead, pass the function to `render` from `svelte/server`:
|
||||
|
||||
```js
|
||||
+++import { render } from 'svelte/server';+++
|
||||
import App from './App.svelte';
|
||||
|
||||
---const { html, head } = App.render({ props: { message: 'hello' }});---
|
||||
+++const { html, head } = render(App, { props: { message: 'hello' }});+++
|
||||
```
|
||||
|
||||
In Svelte 4, rendering a component to a string also returned the CSS of all components. In Svelte 5, this is no longer the case by default because most of the time you're using a tooling chain that takes care of it in other ways (like SvelteKit). If you need CSS to be returned from `render`, you can set the `css` compiler option to `'injected'` and it will add `<style>` elements to the `head`.
|
||||
|
||||
### Component typing changes
|
||||
|
||||
The change from classes towards functions is also reflected in the typings: `SvelteComponent`, the base class from Svelte 4, is deprecated in favour of the new `Component` type which defines the function shape of a Svelte component. To manually define a component shape in a `d.ts` file:
|
||||
|
||||
```ts
|
||||
import type { Component } from 'svelte';
|
||||
export declare const MyComponent: Component<{
|
||||
foo: string;
|
||||
}>;
|
||||
```
|
||||
|
||||
To declare that a component of a certain type is required:
|
||||
|
||||
```svelte
|
||||
<script lang="ts">
|
||||
import type { Component } from 'svelte';
|
||||
import {
|
||||
ComponentA,
|
||||
ComponentB
|
||||
} from 'component-library';
|
||||
|
||||
let component: Component<{ foo: string }> = $state(
|
||||
Math.random() ? ComponentA : ComponentB
|
||||
);
|
||||
</script>
|
||||
|
||||
<svelte:component this={component} foo="bar" />
|
||||
```
|
||||
|
||||
The two utility types `ComponentEvents` and `ComponentType` are also deprecated. `ComponentEvents` is obsolete because events are defined as callback props now, and `ComponentType` is obsolete because the new `Component` type is the component type already (e.g. `ComponentType<SvelteComponent<{ prop: string }>>` == `Component<{ prop: string }>`).
|
||||
|
||||
### bind:this changes
|
||||
|
||||
Because components are no longer classes, using `bind:this` no longer returns a class instance with `$set`, `$on` and `$destroy` methods on it. It only returns the instance exports (`export function/const`) and, if you're using the `accessors` option, a getter/setter-pair for each property.
|
||||
|
||||
## Whitespace handling changed
|
||||
|
||||
Previously, Svelte employed a very complicated algorithm to determine if whitespace should be kept or not. Svelte 5 simplifies this which makes it easier to reason about as a developer. The rules are:
|
||||
|
||||
- Whitespace between nodes is collapsed to one whitespace
|
||||
- Whitespace at the start and end of a tag is removed completely
|
||||
- Certain exceptions apply such as keeping whitespace inside `pre` tags
|
||||
|
||||
As before, you can disable whitespace trimming by setting the `preserveWhitespace` option in your compiler settings or on a per-component basis in `<svelte:options>`.
|
||||
|
||||
## Modern browser required
|
||||
|
||||
Svelte 5 requires a modern browser (in other words, not Internet Explorer) for various reasons:
|
||||
|
||||
- it uses [`Proxies`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
|
||||
- elements with `clientWidth`/`clientHeight`/`offsetWidth`/`offsetHeight` bindings use a [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) rather than a convoluted `<iframe>` hack
|
||||
- `<input type="range" bind:value={...} />` only uses an `input` event listener, rather than also listening for `change` events as a fallback
|
||||
|
||||
The `legacy` compiler option, which generated bulkier but IE-friendly code, no longer exists.
|
||||
|
||||
## Changes to compiler options
|
||||
|
||||
- The `false`/`true` (already deprecated previously) and the `"none"` values were removed as valid values from the `css` option
|
||||
- The `legacy` option was repurposed
|
||||
- The `hydratable` option has been removed. Svelte components are always hydratable now
|
||||
- The `enableSourcemap` option has been removed. Source maps are always generated now, tooling can choose to ignore it
|
||||
- The `tag` option was removed. Use `<svelte:options customElement="tag-name" />` inside the component instead
|
||||
- The `loopGuardTimeout`, `format`, `sveltePath`, `errorMode` and `varsReport` options were removed
|
||||
|
||||
## The `children` prop is reserved
|
||||
|
||||
Content inside component tags becomes a snippet prop called `children`. You cannot have a separate prop by that name.
|
||||
|
||||
## Dot notation indicates a component
|
||||
|
||||
In Svelte 4, `<foo.bar>` would create an element with a tag name of `"foo.bar"`. In Svelte 5, `foo.bar` is treated as a component instead. This is particularly useful inside `each` blocks:
|
||||
|
||||
```svelte
|
||||
{#each items as item}
|
||||
<item.component {...item.props} />
|
||||
{/each}
|
||||
```
|
||||
|
||||
## Breaking changes in runes mode
|
||||
|
||||
Some breaking changes only apply once your component is in runes mode.
|
||||
|
||||
### Bindings to component exports are not allowed
|
||||
|
||||
Exports from runes mode components cannot be bound to directly. For example, having `export const foo = ...` in component `A` and then doing `<A bind:foo />` causes an error. Use `bind:this` instead — `<A bind:this={a} />` — and access the export as `a.foo`. This change makes things easier to reason about, as it enforces a clear separation between props and exports.
|
||||
|
||||
### Bindings need to be explicitly defined using `$bindable()`
|
||||
|
||||
In Svelte 4 syntax, every property (declared via `export let`) is bindable, meaning you can `bind:` to it. In runes mode, properties are not bindable by default: you need to denote bindable props with the `$bindable` rune.
|
||||
|
||||
If a bindable property has a default value (e.g. `let { foo = $bindable('bar') } = $props();`), you need to pass a non-`undefined` value to that property if you're binding to it. This prevents ambiguous behavior — the parent and child must have the same value — and results in better performance (in Svelte 4, the default value was reflected back to the parent, resulting in wasteful additional render cycles).
|
||||
|
||||
### `accessors` option is ignored
|
||||
|
||||
Setting the `accessors` option to `true` makes properties of a component directly accessible on the component instance. In runes mode, properties are never accessible on the component instance. You can use component exports instead if you need to expose them.
|
||||
|
||||
### `immutable` option is ignored
|
||||
|
||||
Setting the `immutable` option has no effect in runes mode. This concept is replaced by how `$state` and its variations work.
|
||||
|
||||
### Classes are no longer "auto-reactive"
|
||||
|
||||
In Svelte 4, doing the following triggered reactivity:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let foo = new Foo();
|
||||
</script>
|
||||
|
||||
<button on:click={() => (foo.value = 1)}>{foo.value}</button
|
||||
>
|
||||
```
|
||||
|
||||
This is because the Svelte compiler treated the assignment to `foo.value` as an instruction to update anything that referenced `foo`. In Svelte 5, reactivity is determined at runtime rather than compile time, so you should define `value` as a reactive `$state` field on the `Foo` class. Wrapping `new Foo()` with `$state(...)` will have no effect — only vanilla objects and arrays are made deeply reactive.
|
||||
|
||||
### `<svelte:component>` is no longer necessary
|
||||
|
||||
In Svelte 4, components are _static_ — if you render `<Thing>`, and the value of `Thing` changes, [nothing happens](https://svelte.dev/repl/7f1fa24f0ab44c1089dcbb03568f8dfa?version=4.2.18). To make it dynamic you must use `<svelte:component>`.
|
||||
|
||||
This is no longer true in Svelte 5:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import A from './A.svelte';
|
||||
import B from './B.svelte';
|
||||
|
||||
let Thing = $state();
|
||||
</script>
|
||||
|
||||
<select bind:value={Thing}>
|
||||
<option value={A}>A</option>
|
||||
<option value={B}>B</option>
|
||||
</select>
|
||||
|
||||
<!-- these are equivalent -->
|
||||
<Thing />
|
||||
<svelte:component this={Thing} />
|
||||
```
|
||||
|
||||
### Touch and wheel events are passive
|
||||
|
||||
When using `onwheel`, `onmousewheel`, `ontouchstart` and `ontouchmove` event attributes, the handlers are [passive](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#using_passive_listeners) to align with browser defaults. This greatly improves responsiveness by allowing the browser to scroll the document immediately, rather than waiting to see if the event handler calls `event.preventDefault()`.
|
||||
|
||||
In the very rare cases that you need to prevent these event defaults, you should use [`on`](/docs/svelte/svelte-events#on) instead (for example inside an action).
|
||||
|
||||
### Attribute/prop syntax is stricter
|
||||
|
||||
In Svelte 4, complex attribute values needn't be quoted:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```svelte
|
||||
<Component prop=this{is}valid />
|
||||
```
|
||||
|
||||
This is a footgun. In runes mode, if you want to concatenate stuff you must wrap the value in quotes:
|
||||
|
||||
```svelte
|
||||
<Component prop="this{is}valid" />
|
||||
```
|
||||
|
||||
Note that Svelte 5 will also warn if you have a single expression wrapped in quotes, like `answer="{42}"` — in Svelte 6, that will cause the value to be converted to a string, rather than passed as a number.
|
||||
|
||||
### HTML structure is stricter
|
||||
|
||||
In Svelte 4, you were allowed to write HTML code that would be repaired by the browser when server side rendering it. For example you could write this...
|
||||
|
||||
```svelte
|
||||
<table>
|
||||
<tr>
|
||||
<td>hi</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
... and the browser would auto-insert a `<tbody>` element:
|
||||
|
||||
```svelte
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>hi</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
```
|
||||
|
||||
Svelte 5 is more strict about the HTML structure and will throw a compiler error in cases where the browser would repair the DOM.
|
||||
|
||||
## Other breaking changes
|
||||
|
||||
### Stricter `@const` assignment validation
|
||||
|
||||
Assignments to destructured parts of a `@const` declaration are no longer allowed. It was an oversight that this was ever allowed.
|
||||
|
||||
### :is(...) and :where(...) are scoped
|
||||
|
||||
Previously, Svelte did not analyse selectors inside `:is(...)` and `:where(...)`, effectively treating them as global. Svelte 5 analyses them in the context of the current component. As such, some selectors may now be treated as unused if they were relying on this treatment. To fix this, use `:global(...)` inside the `:is(...)/:where(...)` selectors.
|
||||
|
||||
When using Tailwind's `@apply` directive, add a `:global` selector to preserve rules that use Tailwind-generated `:is(...)` selectors:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```css
|
||||
main +++:global+++ {
|
||||
@apply bg-blue-100 dark:bg-blue-900;
|
||||
}
|
||||
```
|
||||
|
||||
### CSS hash position no longer deterministic
|
||||
|
||||
Previously Svelte would always insert the CSS hash last. This is no longer guaranteed in Svelte 5. This is only breaking if you [have very weird css selectors](https://stackoverflow.com/questions/15670631/does-the-order-of-classes-listed-on-an-item-affect-the-css).
|
||||
|
||||
### Scoped CSS uses :where(...)
|
||||
|
||||
To avoid issues caused by unpredictable specificity changes, scoped CSS selectors now use `:where(.svelte-xyz123)` selector modifiers alongside `.svelte-xyz123` (where `xyz123` is, as previously, a hash of the `<style>` contents). You can read more detail [here](https://github.com/sveltejs/svelte/pull/10443).
|
||||
|
||||
In the event that you need to support ancient browsers that don't implement `:where`, you can manually alter the emitted CSS, at the cost of unpredictable specificity changes:
|
||||
|
||||
```js
|
||||
// @errors: 2552
|
||||
css = css.replace(/:where\((.+?)\)/, '$1');
|
||||
```
|
||||
|
||||
### Error/warning codes have been renamed
|
||||
|
||||
Error and warning codes have been renamed. Previously they used dashes to separate the words, they now use underscores (e.g. foo-bar becomes foo_bar). Additionally, a handful of codes have been reworded slightly.
|
||||
|
||||
### Reduced number of namespaces
|
||||
|
||||
The number of valid namespaces you can pass to the compiler option `namespace` has been reduced to `html` (the default), `mathml` and `svg`.
|
||||
|
||||
The `foreign` namespace was only useful for Svelte Native, which we're planning to support differently in a 5.x minor.
|
||||
|
||||
### beforeUpdate/afterUpdate changes
|
||||
|
||||
`beforeUpdate` no longer runs twice on initial render if it modifies a variable referenced in the template.
|
||||
|
||||
`afterUpdate` callbacks in a parent component will now run after `afterUpdate` callbacks in any child components.
|
||||
|
||||
Both functions are disallowed in runes mode — use `$effect.pre(...)` and `$effect(...)` instead.
|
||||
|
||||
### `contenteditable` behavior change
|
||||
|
||||
If you have a `contenteditable` node with a corresponding binding _and_ a reactive value inside it (example: `<div contenteditable=true bind:textContent>count is {count}</div>`), then the value inside the contenteditable will not be updated by updates to `count` because the binding takes full control over the content immediately and it should only be updated through it.
|
||||
|
||||
### `oneventname` attributes no longer accept string values
|
||||
|
||||
In Svelte 4, it was possible to specify event attributes on HTML elements as a string:
|
||||
|
||||
```svelte
|
||||
<button onclick="alert('hello')">...</button>
|
||||
```
|
||||
|
||||
This is not recommended, and is no longer possible in Svelte 5, where properties like `onclick` replace `on:click` as the mechanism for adding event handlers.
|
||||
|
||||
### `null` and `undefined` become the empty string
|
||||
|
||||
In Svelte 4, `null` and `undefined` were printed as the corresponding string. In 99 out of 100 cases you want this to become the empty string instead, which is also what most other frameworks out there do. Therefore, in Svelte 5, `null` and `undefined` become the empty string.
|
||||
|
||||
### `bind:files` values can only be `null`, `undefined` or `FileList`
|
||||
|
||||
`bind:files` is now a two-way binding. As such, when setting a value, it needs to be either falsy (`null` or `undefined`) or of type `FileList`.
|
||||
|
||||
### Bindings now react to form resets
|
||||
|
||||
Previously, bindings did not take into account `reset` event of forms, and therefore values could get out of sync with the DOM. Svelte 5 fixes this by placing a `reset` listener on the document and invoking bindings where necessary.
|
||||
|
||||
### `walk` not longer exported
|
||||
|
||||
`svelte/compiler` reexported `walk` from `estree-walker` for convenience. This is no longer true in Svelte 5, import it directly from that package instead in case you need it.
|
||||
|
||||
### Content inside `svelte:options` is forbidden
|
||||
|
||||
In Svelte 4 you could have content inside a `<svelte:options />` tag. It was ignored, but you could write something in there. In Svelte 5, content inside that tag is a compiler error.
|
||||
|
||||
### `<slot>` elements in declarative shadow roots are preserved
|
||||
|
||||
Svelte 4 replaced the `<slot />` tag in all places with its own version of slots. Svelte 5 preserves them in the case they are a child of a `<template shadowrootmode="...">` element.
|
||||
|
||||
### `<svelte:element>` tag must be an expression
|
||||
|
||||
In Svelte 4, `<svelte:element this="div">` is valid code. This makes little sense — you should just do `<div>`. In the vanishingly rare case that you _do_ need to use a literal value for some reason, you can do this:
|
||||
|
||||
```svelte
|
||||
<svelte:element this=+++{+++"div"+++}+++>
|
||||
```
|
||||
|
||||
Note that whereas Svelte 4 would treat `<svelte:element this="input">` (for example) identically to `<input>` for the purposes of determining which `bind:` directives could be applied, Svelte 5 does not.
|
||||
|
||||
### `mount` plays transitions by default
|
||||
|
||||
The `mount` function used to render a component tree plays transitions by default unless the `intro` option is set to `false`. This is different from legacy class components which, when manually instantiated, didn't play transitions by default.
|
||||
|
||||
### `<img src={...}>` and `{@html ...}` hydration mismatches are not repaired
|
||||
|
||||
In Svelte 4, if the value of a `src` attribute or `{@html ...}` tag differ between server and client (a.k.a. a hydration mismatch), the mismatch is repaired. This is very costly: setting a `src` attribute (even if it evaluates to the same thing) causes images and iframes to be reloaded, and reinserting a large blob of HTML is slow.
|
||||
|
||||
Since these mismatches are extremely rare, Svelte 5 assumes that the values are unchanged, but in development will warn you if they are not. To force an update you can do something like this:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let { markup, src } = $props();
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
// stash the values...
|
||||
const initial = { markup, src };
|
||||
|
||||
// unset them...
|
||||
markup = src = undefined;
|
||||
|
||||
$effect(() => {
|
||||
// ...and reset after we've mounted
|
||||
markup = initial.markup;
|
||||
src = initial.src;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{@html markup}
|
||||
<img {src} />
|
||||
```
|
||||
|
||||
### Hydration works differently
|
||||
|
||||
Svelte 5 makes use of comments during server side rendering which are used for more robust and efficient hydration on the client. As such, you shouldn't remove comments from your HTML output if you intend to hydrate it, and if you manually authored HTML to be hydrated by a Svelte component, you need to adjust that HTML to include said comments at the correct positions.
|
||||
|
||||
### `onevent` attributes are delegated
|
||||
|
||||
Event attributes replace event directives: Instead of `on:click={handler}` you write `onclick={handler}`. For backwards compatibility the `on:event` syntax is still supported and behaves the same as in Svelte 4. Some of the `onevent` attributes however are delegated, which means you need to take care to not stop event propagation on those manually, as they then might never reach the listener for this event type at the root.
|
||||
|
||||
### `--style-props` uses a different element
|
||||
|
||||
Svelte 5 uses an extra `<svelte-css-wrapper>` element instead of a `<div>` to wrap the component when using CSS custom properties.
|
||||
|
||||
<!-- TODO in final docs, add link to corresponding section for more details -->
|
@ -1,9 +0,0 @@
|
||||
---
|
||||
title: $state
|
||||
---
|
||||
|
||||
TODO
|
||||
|
||||
- add other pages
|
||||
- figure out a way to get separator titles in here, so we can have a 'runes' section and an 'imports' section and an 'errors/warnings' section without introducing another layer of nesting
|
||||
- figure out a good way to import reference docs from other repos that works locally and in prod
|
Loading…
Reference in new issue