docs: clarify svelte:component migration, avoids common gotcha (#13835)

* docs: clarify svelte:component migration to avoid lowercase component name gotcha

* docs: move <svelte:component> section out of Breaking Changes, since it is not breaking. Change links.

* docs: move migration dot notation component initialization notes to svelte:component section

* tweaks

* fix link

---------

Co-authored-by: Gonzalo Ruiz <rgon@rgon.es>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/14962/head
Gonzalo Ruiz 3 days ago committed by GitHub
parent bf80c10625
commit d15aea4e18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -597,29 +597,58 @@ export declare const MyComponent: Component<{
To declare that a component of a certain type is required: To declare that a component of a certain type is required:
```svelte ```js
<script lang="ts"> import { ComponentA, ComponentB } from 'component-library';
import type { ---SvelteComponent--- +++Component+++ } from 'svelte'; ---import type { SvelteComponent } from 'svelte';---
import { +++import type { Component } from 'svelte';+++
ComponentA,
ComponentB
} from 'component-library';
---let component: typeof SvelteComponent<{ foo: string }>---
+++let component: Component<{ foo: string }>+++ = $state(
Math.random() ? ComponentA : ComponentB
);
</script>
<svelte:component this={component} foo="bar" /> ---let C: typeof SvelteComponent<{ foo: string }> = $state(---
+++let C: Component<{ foo: string }> = $state(+++
Math.random() ? ComponentA : ComponentB
);
``` ```
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 }>`). 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 (i.e. `ComponentType<SvelteComponent<{ prop: string }>>` is equivalent to `Component<{ prop: string }>`).
### bind:this changes ### 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. 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.
## `<svelte:component>` is no longer necessary
In Svelte 4, components are _static_ — if you render `<Thing>`, and the value of `Thing` changes, [nothing happens](/playground/7f1fa24f0ab44c1089dcbb03568f8dfa?version=4.2.18). To make it dynamic you had to 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} />
```
While migrating, keep in mind that your component's name should be capitalized (`Thing`) to distinguish it from elements, unless using dot notation.
### 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}
```
## Whitespace handling changed ## 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: 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:
@ -653,16 +682,6 @@ The `legacy` compiler option, which generated bulkier but IE-friendly code, no l
Content inside component tags becomes a snippet prop called `children`. You cannot have a separate prop by that name. 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 ## Breaking changes in runes mode
Some breaking changes only apply once your component is in runes mode. Some breaking changes only apply once your component is in runes mode.
@ -700,30 +719,6 @@ In Svelte 4, doing the following triggered reactivity:
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. 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](/playground/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 ### 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()`. 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()`.

@ -2,7 +2,7 @@
title: <svelte:component> title: <svelte:component>
--- ---
In runes mode, `<MyComponent>` will re-render if the value of `MyComponent` changes. See the [Svelte 5 migration guide](/docs/svelte/v5-migration-guide#Breaking-changes-in-runes-mode-svelte:component-is-no-longer-necessary) for an example. In runes mode, `<MyComponent>` will re-render if the value of `MyComponent` changes. See the [Svelte 5 migration guide](/docs/svelte/v5-migration-guide#svelte:component-is-no-longer-necessary) for an example.
In legacy mode, it won't — we must use `<svelte:component>`, which destroys and recreates the component instance when the value of its `this` expression changes: In legacy mode, it won't — we must use `<svelte:component>`, which destroys and recreates the component instance when the value of its `this` expression changes:

Loading…
Cancel
Save