More ongoing learnings and new patterns from working with Svelte 5.
💡 This post will serve as a collection of ongoing notes.
💡 Read part 1 of this series on simpler shared runed state, derived state and more.
General Learnings
From studying the migration guide and current GitHub issues for Svelte / Svelte 5 we learn that:
actions
still exist (e.g. for event modifiers)
tick
still exists
event
handling is now more „native“
-
write handler functions that accept an event
object, and handle as intended in the Event API
-
this is even more important for event modifiers that relied on syntactic sugar like event|preventDefault
<script>
const handleEvent(e) => e.preventDefault()
</script>
<button onclick={handleEvent} />
Notes on JSDoc Types & IntelliSense
-
Reminder: there is a component annotation / documentation pattern with @component
- unlike JSDoc typing, it must be within the markup / HTML section
- but it seems you can just put it at the top of the file before the very first
<script>
tag:
<!--
@component
@slot default - The map container with image, vector, and text annotations
-->
<script>
import { getContext } from 'svelte'
import { fade } from 'svelte/transition'
// ...
</script>
<div>your markup</div>
- TBC: if types can be defined / imported / exported here instead of within
-
JSDoc
types can be recycled - exported / imported - when defined in <script module />
(Maintainer comment)
// $lib/ComponentA.svelte
<script module>
/** @typedef {"myType"} MyType */
</script>
<script>
// your logic
</script>
- import and recycle in another component
// $lib/ComponentB.svelte
<script>
/** @import { MyType } from "$lib/ComponentA.svelte" */
/**
* @param {MyType} x
*/
function stuff(x) {
}
</script>
- you can even import in utility libraries /
.js
files (that probably includes .svelte.js
files)
// $utils/+utils.js
/** @import { MyType } from "$lib/ComponentA.svelte" */
/**
* @param {MyType} x
*/
function stuff(x) {
}
Notes on $effect
-
$effect
and onMount
are not the same
onMount()
is only run when the component is mounted.
$effect
is run when
- the component is mounted
- when any of the tracked dependencies are changed
$effect.pre
runs before changes are applied to the DOM
-
observation: it’s unclear if onMount
is supposed to be deprecated or not (probably a Svelte 6 thing)
-
$effect
is not encouraged (Docs, GitHub Issue by Core Maintainer, Tweet by Core Maintainer)
-
$effect
doesn’t run on the server
-
$effect.once()
doesn’t exist yet (svelte@5.1.2), but can be simulated with
import { untrack } from 'svelte';
$effect(() => {
untrack(async () => {
await fetch("/");
})
});
-
async
within $effect
is discouraged (Maintainer)
-
async
within onMount
is discouraged (example)
Notes on $props()
-
to only extract specific properties and collect the rest
let { foo, bar, ...rest } = $props();
-
to collect / preserve all properties: don’t destructure
let props = $props();
<Button {...rest} />
<Button {...props} />
// Button.svelte
<script>
let props = $props();
</script>
<button {...props}>
click me
</button>
-
$bindable
can have a default
let { item = $bindable("default") } = $props()
Reactive Built-Ins: Set, Map, Date, URL, URLSearchParams with Svelte 5
- these so called “built-ins” aren’t reactive off the shelf
- instead, there’s a collection of reactive “built-ins” in
svelte/reactivity
(Docs)
💡 as of svelte@5.1.3:
SvelteDate
SvelteMap
SvelteSet
SvelteURL
SvelteURLSearchParams
TODO / WIP: Standalone modules / UMD / ES6 / IIFE with Vite & Svelte 5
💡 if you don’t know what this means, what this Svelte Summit Talk by Jesse Skinner: https://www.youtube.com/watch?v=uWxkaDdqfpI
TL;DR: Svelte is a compiler; you can compile Svelte components to regular standalone JavaScript modules and mount them wherever you can run JavaScript. You can even pass functions as props and expose component methods to the mounting scope.
- in Svelte 5 components are functions - before, they were Classes (e.g.
new SvelteComponent()
)
- mount with either
mount
or hydrate
hydrate
picks up server-rendered HTML
- you can preserve the CSS with a compiler option:
css: 'injected'
- options now include an
events: {eventType: callback()}
property
tbc: $state
might be used in the instantiating scope
- only in .svelte / .svelte.js
- there’s
unmount
for destroying
- see working example for a server-side-rendered Svelte component using Bun and the Svelte compiler: https://github.com/fubits1/bun-svelte-ssr
What I don’t understand yet
-
$state.snapshot
💡 why and how and what for?
$effect( () => {
// Get a snapshot :
const data = $state.snapshot(config);
// Save data
// ...
console.log(data);
});
-
level or granularity of shared / bindable reactivity (example GitHub issue)
-
migration guide: „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.” (Svelte 5 Migration Guide)
💡 to be continued