Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync svelte docs #1196

Merged
merged 1 commit into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 7 additions & 35 deletions apps/svelte.dev/content/docs/svelte/02-runes/04-$effect.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,62 +281,34 @@ You might be tempted to do something convoluted with effects to link one value t
</label>
```

Instead, use callbacks where possible ([demo](/playground/untitled#H4sIAAAAAAAACo1SMW6EMBD8imWluFMSIEUaDiKlvy5lSOHjlhOSMRZeTiDkv8deMEEJRcqdmZ1ZjzzxqpZgePo5cRw18JQA_sSVaPz0rnVk7iDRYxdhYA8vW4Wg0NnwzJRdrfGtUAVKQIYtCsly9pIkp4AZ7cQOezAoEA7JcWUkVBuCdol0dNWrEutWsV5fHfnhPQ5wZJMnCwyejxCh6G6A0V3IHk4zu_jOxzzPBxBld83PTr7xXrb3rUNw8PbiYJ3FP22oTIoLSComq5XuXTeu8LzgnVA3KDgj13wiQ8taRaJ82rzXskYM-URRlsXktejjgNLoo9e4fyf70_8EnwncySX1GuunX6kGRwnzR_BgaPNaGy3FmLJKwrCUeBM6ZUn0Cs2mOlp3vwthQJ5i14P9st9vZqQlsQIAAA==)):
Instead, use `oninput` callbacks or — better still — [function bindings](bind#Function-bindings) where possible ([demo](/playground/untitled#H4sIAAAAAAAAE51SsW6DMBT8FcvqABINdOhCIFKXTt06lg4GHpElYyz8iECIf69tcIIipo6-u3f3fPZMJWuBpvRzkBXyTpKSy5rLq6YRbbgATdOfmeKkrMgCBt9GPpQ66RsItFjJNBzhVScRJBobmumq5wovhSxQABLskAmSk7ckOXtMKyM22ItGhhAk4Z0R0OwIN-tIQzd-90HVhvy2HsGNiQFCMltBgd7XoecV2xzXNV7XaEcth7ZfRv7kujnsTX2Qd7USb5rFjwZkJlgJwpWRcakG04cpOS9oz-QVCuoeInXW-RyEJL-sG0b7Wy6kZWM-u7CFxM5tdrIl9qg72vB74H-y7T2iXROHyVb0CLanp1yNk4D1A1jQ91hzrQSbUtIIGLcir0ylJDm9Q7urz42bX4UwIk2xH2D5Xf4A7SeMcMQCAAA=)):

```svelte
<script>
let total = 100;
let spent = $state(0);
let left = $state(total);

function updateSpent(e) {
spent = +e.target.value;
function updateSpent(value) {
spent = value;
left = total - spent;
}

function updateLeft(e) {
left = +e.target.value;
function updateLeft(value) {
left = value;
spent = total - left;
}
</script>

<label>
<input type="range" value={spent} oninput={updateSpent} max={total} />
<input type="range" bind:value={() => spent, updateSpent} max={total} />
{spent}/{total} spent
</label>

<label>
<input type="range" value={left} oninput={updateLeft} max={total} />
<input type="range" bind:value={() => left, updateLeft} max={total} />
{left}/{total} left
</label>
```

If you need to use bindings, for whatever reason (for example when you want some kind of "writable `$derived`"), consider using getters and setters to synchronise state ([demo](/playground/untitled#H4sIAAAAAAAACpWRwW6DMBBEf8WyekikFOihFwcq9TvqHkyyQUjGsfCCQMj_XnvBNKpy6Qn2DTOD1wu_tRocF18Lx9kCFwT4iRvVxenT2syNoDGyWjl4xi93g2AwxPDSXfrW4oc0EjUgwzsqzSr2VhTnxJwNHwf24lAhHIpjVDZNwy1KS5wlNoGMSg9wOCYksQccerMlv65p51X0p_Xpdt_4YEy9yTkmV3z4MJT579-bUqsaNB2kbI0dwlnCgirJe2UakJzVrbkKaqkWivasU1O1ULxnOVk3JU-Uxti0p_-vKO4no_enbQ_yXhnZn0aHs4b1jiJMK7q2zmo1C3bTMG3LaZQVrMjeoSPgaUtkDxePMCEX2Ie6b_8D4WyJJEwCAAA=)):

```svelte
<script>
let total = 100;
let spent = $state(0);

let left = {
get value() {
return total - spent;
},
set value(v) {
spent = total - v;
}
};
</script>

<label>
<input type="range" bind:value={spent} max={total} />
{spent}/{total} spent
</label>

<label>
<input type="range" bind:value={left.value} max={total} />
{left.value}/{total} left
</label>
```

If you absolutely have to update `$state` within an effect and run into an infinite loop because you read and write to the same `$state`, use [untrack](svelte#untrack).
164 changes: 86 additions & 78 deletions apps/svelte.dev/content/docs/svelte/06-runtime/02-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,129 +3,137 @@ NOTE: do not edit this file, it is generated in apps/svelte.dev/scripts/sync-doc
title: Context
---

<!-- - get/set/hasContext
- how to use, best practises (like encapsulating them) -->
Context allows components to access values owned by parent components without passing them down as props (potentially through many layers of intermediate components, known as 'prop-drilling'). The parent component sets context with `setContext(key, value)`...

Most state is component-level state that lives as long as its component lives. There's also section-wide or app-wide state however, which also needs to be handled somehow.

The easiest way to do that is to create global state and just import that.
```svelte
<!--- file: Parent.svelte --->
<script>
import { setContext } from 'svelte';

```ts
/// file: state.svelte.js
export const myGlobalState = $state({
user: {
/* ... */
}
/* ... */
});
setContext('my-context', 'hello from Parent.svelte');
</script>
```

...and the child retrieves it with `getContext`:

```svelte
<!--- file: App.svelte --->
<!--- file: Child.svelte --->
<script>
import { myGlobalState } from './state.svelte.js';
// ...
import { getContext } from 'svelte';

const message = getContext('my-context');
</script>

<h1>{message}, inside Child.svelte</h1>
```

This has a few drawbacks though:
This is particularly useful when `Parent.svelte` is not directly aware of `Child.svelte`, but instead renders it as part of a `children` [snippet](snippet) ([demo](/playground/untitled#H4sIAAAAAAAAE42Q3W6DMAyFX8WyJgESK-oto6hTX2D3YxcM3IIUQpR40yqUd58CrCXsp7tL7HNsf2dAWXaEKR56yfTBGOOxFWQwfR6Qz8q1XAHjL-GjUhvzToJd7bU09FO9ctMkG0wxM5VuFeeFLLjtVK8ZnkpNkuGo-w6CTTJ9Z3PwsBAemlbUF934W8iy5DpaZtOUcU02-ZLcaS51jHEkTFm_kY1_wfOO8QnXrb8hBzDEc6pgZ4gFoyz4KgiD7nxfTe8ghqAhIfrJ46cTzVZBbkPlODVJsLCDO6V7ZcJoncyw1yRr0hd1GNn_ZbEM3I9i1bmVxOlWElUvDUNHxpQngt3C4CXzjS1rtvkw22wMrTRtTbC8Lkuabe7jvthPPe3DofYCAAA=)):

```svelte
<Parent>
<Child />
</Parent>
```

- it only safely works when your global state is only used client-side - for example, when you're building a single page application that does not render any of your components on the server. If your state ends up being managed and updated on the server, it could end up being shared between sessions and/or users, causing bugs
- it may give the false impression that certain state is global when in reality it should only used in a certain part of your app
The key (`'my-context'`, in the example above) and the context itself can be any JavaScript value.

To solve these drawbacks, Svelte provides a few `context` primitives which alleviate these problems.
In addition to [`setContext`](svelte#setContext) and [`getContext`](svelte#getContext), Svelte exposes [`hasContext`](svelte#hasContext) and [`getAllContexts`](svelte#getAllContexts) functions.

## Setting and getting context
## Using context with state

To associate an arbitrary object with the current component, use `setContext`.
You can store reactive state in context ([demo](/playground/untitled#H4sIAAAAAAAAE41R0W6DMAz8FSuaBNUQdK8MkKZ-wh7HHihzu6hgosRMm1D-fUpSVNq12x4iEvvOx_kmQU2PIhfP3DCCJGgHYvxkkYid7NCI_GUS_KUcxhVEMjOelErNB3bsatvG4LW6n0ZsRC4K02qpuKqpZtmrQTNMYJA3QRAs7PTQQxS40eMCt3mX3duxnWb-lS5h7nTI0A4jMWoo4c44P_Hku-zrOazdy64chWo-ScfRkRgl8wgHKrLTH1OxHZkHgoHaTraHcopXUFYzPPVfuC_hwQaD1GrskdiNCdQwJljJqlvXfyqVsA5CGg0uRUQifHw56xFtciO75QrP07vo_JXf_tf8yK2ezDKY_ZWt_1y2qqYzv7bI1IW1V_sN19m-07wCAAA=))...

```svelte
<script>
import { setContext } from 'svelte';
import Child from './Child.svelte';

setContext('key', value);
let counter = $state({
count: 0
});

setContext('counter', counter);
</script>

<button onclick={() => counter.count += 1}>
increment
</button>

<Child />
<Child />
<Child />
```

The context is then available to children of the component (including slotted content) with `getContext`.
...though note that if you _reassign_ `counter` instead of updating it, you will 'break the link' — in other words instead of this...

```svelte
<script>
import { getContext } from 'svelte';

const value = getContext('key');
</script>
<button onclick={() => counter = { count: 0 }}>
reset
</button>
```

`setContext` and `getContext` solve the above problems:
...you must do this:

- the state is not global, it's scoped to the component. That way it's safe to render your components on the server and not leak state
- it's clear that the state is not global but rather scoped to a specific component tree and therefore can't be used in other parts of your app
```svelte
<button onclick={() => +++counter.count = 0+++}>
reset
</button>
```

> [!NOTE] `setContext`/`getContext` must be called during component initialisation.
Svelte will warn you if you get it wrong.

Context is not inherently reactive. If you need reactive values in context then you can pass a `$state` object into context, whose properties _will_ be reactive.
## Type-safe context

```svelte
<!--- file: Parent.svelte --->
<script>
import { setContext } from 'svelte';
A useful pattern is to wrap the calls to `setContext` and `getContext` inside helper functions that let you preserve type safety:

let value = $state({ count: 0 });
setContext('counter', value);
</script>
```js
/// file: context.js
// @filename: ambient.d.ts
interface User {}

<button onclick={() => value.count++}>increment</button>
```
// @filename: index.js
// ---cut---
import { getContext, setContext } from 'svelte';

```svelte
<!--- file: Child.svelte --->
<script>
import { getContext } from 'svelte';
let key = {};

const value = getContext('counter');
</script>
/** @param {User} user */
export function setUserContext(user) {
setContext(key, user);
}

<p>Count is {value.count}</p>
export function getUserContext() {
return /** @type {User} */ (getContext(key));
}
```

To check whether a given `key` has been set in the context of a parent component, use `hasContext`.
## Replacing global state

```svelte
<script>
import { hasContext } from 'svelte';
When you have state shared by many different components, you might be tempted to put it in its own module and just import it wherever it's needed:

if (hasContext('key')) {
// do something
```js
/// file: state.svelte.js
export const myGlobalState = $state({
user: {
// ...
}
</script>
// ...
});
```

You can also retrieve the whole context map that belongs to the closest parent component using `getAllContexts`. This is useful, for example, if you programmatically create a component and want to pass the existing context to it.
In many cases this is perfectly fine, but there is a risk: if you mutate the state during server-side rendering (which is discouraged, but entirely possible!)...

```svelte
<!--- file: App.svelte ---->
<script>
import { getAllContexts } from 'svelte';
import { myGlobalState } from 'svelte';

const contexts = getAllContexts();
let { data } = $props();

if (data.user) {
myGlobalState.user = data.user;
}
</script>
```

## Encapsulating context interactions

The above methods are very unopinionated about how to use them. When your app grows in scale, it's worthwhile to encapsulate setting and getting the context into functions and properly type them.

```ts
// @errors: 2304
import { getContext, setContext } from 'svelte';

let userKey = Symbol('user');

export function setUserContext(user: User) {
setContext(userKey, user);
}

export function getUserContext(): User {
return getContext(userKey) as User;
}
```
...then the data may be accessible by the _next_ user. Context solves this problem because it is not shared between requests.
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ You don't have to migrate to the new syntax right away - Svelte 5 still supports

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
### 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+++)+++;
let count = +++$state(0)+++;
</script>
```

Expand All @@ -26,14 +26,14 @@ Nothing else changes. `count` is still the number itself, and you read and write
> [!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 outside the top level of components. Head to [the tutorial](/tutorial) to learn more.

### $: -> $derived/$effect
### $: $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+++)+++;
let count = $state(0);
---$:--- +++const+++ double = +++$derived(count * 2)+++;
</script>
```

Expand All @@ -43,7 +43,8 @@ A `$:` statement could also be used to create side effects. In Svelte 5, this is

```svelte
<script>
let count = +++$state(+++0+++)+++;
let count = $state(0);

---$:---+++$effect(() =>+++ {
if (count > 5) {
alert('Count is too high!');
Expand Down Expand Up @@ -74,14 +75,14 @@ Note that [when `$effect` runs is different]($effect#Understanding-dependencies)
> - executing dependencies as needed and therefore being immune to ordering problems
> - being TypeScript-friendly

### export let -> $props
### 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;---
---export let optional = 'unset';---
---export let required;---
+++let { optional = 'unset', required } = $props();+++
</script>
```
Expand All @@ -106,8 +107,8 @@ In Svelte 5, the `$props` rune makes this straightforward without any additional

```svelte
<script>
---let klass = '';
export { klass as class};---
---let klass = '';---
---export { klass as class};---
+++let { class: klass, ...rest } = $props();+++
</script>
<button class={klass} {...---$$restProps---+++rest+++}>click me</button>
Expand Down Expand Up @@ -193,9 +194,9 @@ This function is deprecated in Svelte 5. Instead, components should accept _call
```svelte
<!--- file: Pump.svelte --->
<script>
---import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
---
---import { createEventDispatcher } from 'svelte';---
---const dispatch = createEventDispatcher();---

+++let { inflate, deflate } = $props();+++
let power = $state(5);
</script>
Expand Down Expand Up @@ -467,11 +468,11 @@ By now you should have a pretty good understanding of the before/after and how t
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, ...)`)
- 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.

Expand Down
Loading