Skip to content

Commit d15aea4

Browse files
rgonGonzalo RuizRich-Harris
authored
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 <[email protected]> Co-authored-by: Rich Harris <[email protected]>
1 parent bf80c10 commit d15aea4

File tree

2 files changed

+45
-50
lines changed

2 files changed

+45
-50
lines changed

documentation/docs/07-misc/07-v5-migration-guide.md

Lines changed: 44 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -597,29 +597,58 @@ export declare const MyComponent: Component<{
597597

598598
To declare that a component of a certain type is required:
599599

600-
```svelte
601-
<script lang="ts">
602-
import type { ---SvelteComponent--- +++Component+++ } from 'svelte';
603-
import {
604-
ComponentA,
605-
ComponentB
606-
} from 'component-library';
607-
608-
---let component: typeof SvelteComponent<{ foo: string }>---
609-
+++let component: Component<{ foo: string }>+++ = $state(
610-
Math.random() ? ComponentA : ComponentB
611-
);
612-
</script>
600+
```js
601+
import { ComponentA, ComponentB } from 'component-library';
602+
---import type { SvelteComponent } from 'svelte';---
603+
+++import type { Component } from 'svelte';+++
613604

614-
<svelte:component this={component} foo="bar" />
605+
---let C: typeof SvelteComponent<{ foo: string }> = $state(---
606+
+++let C: Component<{ foo: string }> = $state(+++
607+
Math.random() ? ComponentA : ComponentB
608+
);
615609
```
616610
617-
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 }>`).
611+
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 }>`).
618612
619613
### bind:this changes
620614
621615
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.
622616

617+
## `<svelte:component>` is no longer necessary
618+
619+
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>`.
620+
621+
This is no longer true in Svelte 5:
622+
623+
```svelte
624+
<script>
625+
import A from './A.svelte';
626+
import B from './B.svelte';
627+
628+
let Thing = $state();
629+
</script>
630+
631+
<select bind:value={Thing}>
632+
<option value={A}>A</option>
633+
<option value={B}>B</option>
634+
</select>
635+
636+
<!-- these are equivalent -->
637+
<Thing />
638+
<svelte:component this={Thing} />
639+
```
640+
While migrating, keep in mind that your component's name should be capitalized (`Thing`) to distinguish it from elements, unless using dot notation.
641+
642+
### Dot notation indicates a component
643+
644+
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:
645+
646+
```svelte
647+
{#each items as item}
648+
<item.component {...item.props} />
649+
{/each}
650+
```
651+
623652
## Whitespace handling changed
624653
625654
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
653682
654683
Content inside component tags becomes a snippet prop called `children`. You cannot have a separate prop by that name.
655684
656-
## Dot notation indicates a component
657-
658-
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:
659-
660-
```svelte
661-
{#each items as item}
662-
<item.component {...item.props} />
663-
{/each}
664-
```
665-
666685
## Breaking changes in runes mode
667686
668687
Some breaking changes only apply once your component is in runes mode.
@@ -700,30 +719,6 @@ In Svelte 4, doing the following triggered reactivity:
700719
701720
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.
702721
703-
### `<svelte:component>` is no longer necessary
704-
705-
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>`.
706-
707-
This is no longer true in Svelte 5:
708-
709-
```svelte
710-
<script>
711-
import A from './A.svelte';
712-
import B from './B.svelte';
713-
714-
let Thing = $state();
715-
</script>
716-
717-
<select bind:value={Thing}>
718-
<option value={A}>A</option>
719-
<option value={B}>B</option>
720-
</select>
721-
722-
<!-- these are equivalent -->
723-
<Thing />
724-
<svelte:component this={Thing} />
725-
```
726-
727722
### Touch and wheel events are passive
728723
729724
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()`.

documentation/docs/99-legacy/30-legacy-svelte-component.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: <svelte:component>
33
---
44

5-
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.
5+
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.
66

77
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:
88

0 commit comments

Comments
 (0)