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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ A `<script>` tag with a `module` attribute runs once when the module first evalu

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.

> [!LEGACY]
> In Svelte 4, this script tag was created using `<script context="module">`

## `<style>`

CSS inside a `<style>` block will be scoped to that component.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ 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.

> [!LEGACY]
> This is a concept that didn't exist prior to Svelte 5
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ 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)

> [!LEGACY]
> Runes didn't exist prior to Svelte 5.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ A lowercase tag, like `<div>`, denotes a regular HTML element. A capitalised tag
</div>
```

## Attributes and props
## Element attributes

By default, attributes work exactly like their HTML counterparts.

Expand Down Expand Up @@ -72,6 +72,8 @@ When the attribute name and value match (`name={name}`), they can be replaced wi
-->
```

## Component props

By convention, values passed to components are referred to as _properties_ or _props_ rather than _attributes_, which are a feature of the DOM.

As with elements, `name={name}` can be replaced with the `{name}` shorthand.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ document.body.innerHTML = `
`;
```

Any [props](basic-markup#Attributes-and-props) are exposed as properties of the DOM element (as well as being readable/writable as attributes, where possible).
Any [props](basic-markup#Component-props) are exposed as properties of the DOM element (as well as being readable/writable as attributes, where possible).

```js
// @noErrors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ For `$set`, use `$state` instead to create a reactive property object and manipu
import App from './App.svelte'

---const app = new App({ target: document.getElementById("app"), props: { foo: 'bar' } });
app.$set('event', { foo: 'baz' });---
app.$set({ foo: 'baz' });---
+++const props = $state({ foo: 'bar' });
const app = mount(App, { target: document.getElementById("app"), props });
props.foo = 'baz';+++
Expand Down Expand Up @@ -850,7 +850,7 @@ In Svelte 4, `null` and `undefined` were printed as the corresponding string. In

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
### `walk` no 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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The `render` function passed to `createRawSnippet` should return HTML for a sing
### legacy_recursive_reactive_block

```
Detected a migrated `$:` reactive block that both accesses and updates the same reactive value. This may cause recursive updates when converted to an `$effect`.
Detected a migrated `$:` reactive block in `%filename%` that both accesses and updates the same reactive value. This may cause recursive updates when converted to an `$effect`.
```

### lifecycle_double_unmount
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Overview
---

Svelte 5 introduced some significant changes to Svelte's API, including [runes](what-are-runes), [snippets](snippet) and event attributes. As a result, some Svelte 3/4 features are deprecated (though supported for now, unless otherwise specified) and will eventually be removed. We recommend that you incrementally [migrate your existing code](v5-migration-guide).

The following pages document these features for

- people still using Svelte 3/4
- people using Svelte 5, but with components that haven't yet been migrated

Since Svelte 3/4 syntax still works in Svelte 5, we will distinguish between _legacy mode_ and _runes mode_. Once a component is in runes mode (which you can opt into by using runes, or by explicitly setting the `runes: true` compiler option), legacy mode features are no longer available.
34 changes: 34 additions & 0 deletions apps/svelte.dev/content/docs/svelte/99-legacy/01-legacy-let.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
title: Reactive let/var declarations
---

In runes mode, reactive state is explicitly declared with the [`$state` rune]($state).

In legacy mode, variables declared at the top level of a component are automatically considered _reactive_. Reassigning or mutating these variables (`count += 1` or `object.x = y`) will cause the UI to update.

```svelte
<script>
let count = 0;
</script>

<button on:click={() => count += 1}>
clicks: {count}
</button>
```

Because Svelte's legacy mode reactivity is based on _assignments_, using array methods like `.push()` and `.splice()` won't automatically trigger updates. A subsequent assignment is required to 'tell' the compiler to update the UI:

```svelte
<script>
let numbers = [1, 2, 3, 4];

function addNumber() {
// this method call does not trigger an update
numbers.push(numbers.length + 1);

// this assignment will update anything
// that depends on `numbers`
numbers = numbers;
}
</script>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
title: Reactive $: statements
---

In runes mode, reactions to state updates are handled with the [`$derived`]($derived) and [`$effect`]($effect) runes.

In legacy mode, any top-level statement (i.e. not inside a block or a function) can be made reactive by prefixing it with a `$:` [label](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label). These statements run after other code in the `<script>` and before the component markup is rendered, then whenever the values that they depend on change.

```svelte
<script>
let a = 1;
let b = 2;

// this is a 'reactive statement', and it will re-run
// when `a`, `b` or `sum` change
$: console.log(`${a} + ${b} = ${sum}`);

// this is a 'reactive assignment' — `sum` will be
// recalculated when `a` or `b` change. It is
// not necessary to declare `sum` separately
$: sum = a + b;
</script>
```

Statements are ordered _topologically_ by their dependencies and their assignments: since the `console.log` statement depends on `sum`, `sum` is calculated first even though it appears later in the source.

Multiple statements can be combined by putting them in a block:

```js
// @noErrors
$: {
// recalculate `total` when `items` changes
total = 0;

for (const item of items) {
total += item.value;
}
}
```

The left-hand side of a reactive assignments can be an identifier, or it can be a destructuring assignment:

```js
// @noErrors
$: ({ larry, moe, curly } = stooges);
```

## Understanding dependencies

The dependencies of a `$:` statement are determined at compile time — they are whichever variables are referenced (but not assigned to) inside the statement.

In other words, a statement like this will _not_ re-run when `count` changes, because the compiler cannot 'see' the dependency:

```js
// @noErrors
let count = 0;
let double = () => count * 2;

$: doubled = double();
```

Similarly, topological ordering will fail if dependencies are referenced indirectly: `z` will never update, because `y` is not considered 'dirty' when the update occurs. Moving `$: z = y` below `$: setY(x)` will fix it:

```svelte
<script>
let x = 0;
let y = 0;

$: z = y;
$: setY(x);

function setY(value) {
y = value;
}
</script>
```

## Browser-only code

Reactive statements run during server-side rendering as well as in the browser. This means that any code that should only run in the browser must be wrapped in an `if` block:

```js
// @noErrors
$: if (browser) {
document.title = title;
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
title: export let
---

In runes mode, [component props](basic-markup#Component-props) are declared with the [`$props`]($props) rune, allowing parent components to pass in data.

In legacy mode, props are marked with the `export` keyword, and can have a default value:

```svelte
<script>
export let foo;
export let bar = 'default value';

// Values that are passed in as props
// are immediately available
console.log({ foo });
</script>
```

The default value is used if it would otherwise be `undefined` when the component is created.

> [!NOTE] Unlike in runes mode, if the parent component changes a prop from a defined value to `undefined`, it does not revert to the initial value.

Props without default values are considered _required_, and Svelte will print a warning during development if no value is provided, which you can squelch by specifying `undefined` as the default value:

```js
export let foo +++= undefined;+++
```

## Component exports

An exported `const`, `class` or `function` declaration is _not_ considered a prop — instead, it becomes part of the component's API:

```svelte
<!--- file: Greeter.svelte--->
<script>
export function greet(name) {
alert(`hello ${name}!`);
}
</script>
```

```svelte
<!--- file: App.svelte --->
<script>
import Greeter from './Greeter.svelte';

let greeter;
</script>

<Greeter bind:this={greeter} />

<button on:click={() => greeter.greet('world')}>
greet
</button>
```

## Renaming props

The `export` keyword can appear separately from the declaration. This is useful for renaming props, for example in the case of a reserved word:

```svelte
<!--- file: App.svelte --->
<script>
/** @type {string} */
let className;

// creates a `class` property, even
// though it is a reserved word
export { className as class };
</script>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: $$props and $$restProps
---

In runes mode, getting an object containing all the props that were passed in is easy, using the [`$props`]($props) rune.

In legacy mode, we use `$$props` and `$$restProps`:

- `$$props` contains all the props that were passed in, including ones that are not individually declared with the `export` keyword
- `$$restProps` contains all the props that were passed in _except_ the ones that were individually declared

For example, a `<Button>` component might need to pass along all its props to its own `<button>` element, except the `variant` prop:

```svelte
<script>
export let variant;
</script>

<button {...$$restProps} class="variant-{variant} {$$props.class ?? ''}">
click me
</button>

<style>
.variant-danger {
background: red;
}
</style>
```

In Svelte 3/4 using `$$props` and `$$restProps` creates a modest performance penalty, so they should only be used when needed.
Loading
Loading