Skip to content

Search documentation

Find Svelte Patterns and Svelte reference pages.

No results for ""

Try a shorter query or a related Svelte term.

We outline some common gotchas or un-intuitative bahaviour in svelte

In svelte 4, you had to do this.

<script>
	let arr = [0, 1]

	function handleClick() {
		arr.push(2)
		arr = arr // [!code highlight]
	}
</script>

https://v4.svelte.dev/docs/svelte-components#script-2-assignments-are-reactive

It was unintuitive that you had to reassign arrays and objects to trigger reactivity. Svelte 5 also has its own set of unintuitive patterns. Here, I will attempt to document them.

# Destructuring can break reactivity

Either use data.foo or add a $derived rune.

<script>
	let { data } = $props()
	const { foo } = data // [!code --]
	const { foo } = $derived(data) // [!code ++]
</script>

{foo}

Example

In JavaScript, destructured declarations are evaluated at the time of destructuring. src: https://github.com/sveltejs/svelte/issues/11911#issuecomment-2195478261

# Intermediate variables can break reactivity

Either use data.foo or add a $derived rune.

todo: seperate intermediate and destructure

<script>
	let { data } = $props()
	const foo = data.foo // [!code --]
	const foo = $derived(data.foo) // [!code ++]
</script>

{foo}

Example

# data in $state will not receive upstream updates

<script>
	let { data } = $props()
	const foo = $state(data.foo) // [!code --]
	const foo = $derived(data.foo) // [!code ++]
</script>

{foo}

# Module exports are immutable

$state should be used with an object so it can be mutated.

//util.svelte.js
export const value = $state(5) // [!code --]
export const value = $state({ current: 5 }) // [!code ++]

# Direct DOM maniputation will desync the Svelte’s runtime

Let svelte change the DOM

<script>
	let value = $state(0)
</script>

<button onclick={(e) => (e.currentTarget.innerHTML = 4)}>{value}</button> // [!code --]
<button onclick={(e) => (value = 4)}>{value}</button> // [!code ++]

# Avoid using browser globals

<script>
	const input = document.getElementById('id') // [!code --]
	let input // [!code ++]
</script>

<input id="id" /> // [!code --]
<input bind:this={input} /> // [!code ++]
<script>
	const handler = () => console.log('Boo!')
	const input = document.getElementById('button') // [!code --]
	button.addEventListener('click', handler) // [!code --]
</script>

<button id="button">click to see</button> // [!code --]
<button onclick={handler}>click to see<button> // [!code ++]
<script>
	const onresize = () => console.log('Window resized!')
	window.addEventListener('resize', onresize) // [!code --]
</script>

<svelte:window {onresize} /> // [!code ++]