svelte-hydration-repro Svelte Themes

Svelte Hydration Repro

Svelte 5 hydration bug: {@const} + Map.get() inside async {#each} with $derived(await query(...))

Svelte 5 Hydration Crash: {@const} + Map.get() inside async {#each}

Bug

Using {@const Component = map.get(item.type)} inside an {#each} block that iterates over data from $derived(await query(...)) crashes during client-side hydration with:

TypeError: can't access property "f" of undefined

The "f" refers to internal signal flags (SIGNAL_FLAGS). The {@const} declarations create intermediate derived signals in the compiled output that become undefined when the hydration reactive update runs.

Environment

  • Svelte 5.53.7+
  • SvelteKit 2.53.4+
  • compilerOptions.experimental.async: true
  • kit.experimental.remoteFunctions: true

Reproduce

cd repro
npm install   # or bun install
npm run dev
# Open http://localhost:5173 — observe hydration error in browser console

The error reproduces reliably in dev mode. Open the browser console and you'll see multiple:

TypeError: Cannot read properties of undefined (reading 'f')

Then navigate to /workaround — same data, same components, zero errors.

Pages

  • /Buggy: Uses {@const Component = map.get(type)} pattern → hydration crash
  • /workaroundWorking: Uses explicit {#if} conditionals → no crash

Root Cause

The compiled SSR + client output for {@const} inside an async-derived {#each} creates signal indirections like:

// Compiled from: {@const Component = components.get(item.type)}
const Component = derived(() => components.get(get(item).type))

During hydration, when the async boundary resolves and triggers a reactive update, these intermediate derived signals can be accessed before they're properly initialized, causing get(signal) to access .f (flags) on undefined.

Replacing the dynamic lookup with explicit {#if item.type === 'card-a'} branches avoids creating these intermediate signals entirely.

Minimal Pattern

<script>
  import A from './A.svelte'
  import B from './B.svelte'
  import { getData } from './data.remote'
  import { page } from '$app/state'

  // async derived — triggers the bug
  let { items } = $derived(await getData({ url: page.url }))

  const components = new Map([['a', A], ['b', B]])
</script>

<!-- CRASHES during hydration -->
{#each items as item (item.id)}
  {@const Component = components.get(item.type)}
  <Component {...item.props} />
{/each}

<!-- WORKS — no intermediate derived signals -->
{#each items as item (item.id)}
  {#if item.type === 'a'}
    <A {...item.props} />
  {:else if item.type === 'b'}
    <B {...item.props} />
  {/if}
{/each}

Top categories

Loading Svelte Themes