Svelte

Hydration mismatch and TypeError when combining $derived(await ...) with {@attach} from bind:this

#17608

Development PRs

Issue

Solved
E
exentrich
Feb 3, 2026, 9:01 AM

Describe the bug

TL;DR: Using experimental async ($derived(await ...)) in a component that also uses {@attach} with a function from bind:this breaks SSR hydration. In development, this causes a warning. In production build with nested components, the application crashes completely and the page fails to render.

The issue appears to be that the async boundary interacts incorrectly with the {@attach} directive during hydration. When the component contains $derived(await ...), the hydration process seems to lose track of the attachment state, causing a mismatch between server-rendered HTML and client-side component tree. With nested {@render children?.()} calls, this mismatch escalates into a fatal TypeError that prevents the component from mounting.

Conditions to reproduce

All of the following must be present:

  1. Experimental async enabled in svelte.config.js: compilerOptions: { experimental: { async: true } }

  2. $derived(await ...) in the component (even if the value is not used in markup)

  3. {@attach ref?.action} where:

  • ref is obtained via bind:this from another component
  • The function is exported from that component
  1. SSR enabled (default in SvelteKit)

  2. Nested components with {@render children?.()} — for TypeError to occur, at least two levels of nesting required

Behavior

EnvironmentSingle nestingDouble nesting
Devhydration_mismatch warninghydration_mismatch warning
Productionhydration_mismatch warningApplication crashes, component fails to render

Notes

  • Removing $derived(await ...) — no error
  • Removing {@attach} — no error
  • Removing bind:this reference (using local function instead) — no error
  • Disabling SSR — no error

Expected

No hydration mismatch when the async value is not used in the SSR-rendered markup, and no TypeError regardless of component nesting depth.

Reproduction

svelte.config.js

export default {
  compilerOptions: {
    experimental: { async: true }
  }
}

index.svelte

<script>
  import Outer from './Outer.svelte'
  import Inner from './Inner.svelte'
  import Trigger from './Trigger.svelte'

  const data = $derived(await Promise.resolve(['a', 'b']))
  let trigger = $state()
</script>

<Outer>
  <Inner {@attach trigger?.action}>
    foo
  </Inner>
</Outer>

<Trigger bind:this={trigger} />

Outer.svelte

<script>
  let { children } = $props()
</script>
<div>{@render children?.()}</div>

Inner.svelte

<script>
  let { children } = $props()
</script>
<div>{@render children?.()}</div>

Trigger.svelte

<script lang='ts'>
  import type { Attachment } from 'svelte/attachments'

  export function action(): Attachment<HTMLElement> {
    return ()=>{}
  }
</script>

Logs

https://svelte.dev/e/hydration_mismatch
TypeError: Cannot read properties of undefined (reading 'f')

System Info

System:
    OS: macOS 26.2
    CPU: (10) arm64 Apple M1 Pro
    Memory: 147.34 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 24.13.0 - /Users/exentrich/.nvm/versions/node/v24.13.0/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 11.6.2 - /Users/exentrich/.nvm/versions/node/v24.13.0/bin/npm
    bun: 1.3.7 - /Users/exentrich/.bun/bin/bun
    Deno: 2.0.4 - /Users/exentrich/.deno/bin/deno
  Browsers:
    Brave Browser: 141.1.83.120
    Chrome: 144.0.7559.110
    Chrome Canary: 146.0.7666.1
    Edge: 142.0.3595.80
    Firefox: 146.0.1
    Safari: 26.2
    Safari Technology Preview: 26.0
  npmPackages:
    svelte: ^5.48.2 => 5.49.1

Severity

blocking an upgrade

👍 1

Info

Closed at Feb 6, 2026, 9:19 AM
Assignees None
Labels None
Milestone None

Pro tip: You can prefix GitHub URLs of issues, PRs or discussions with svcl.dev/ to view them on this page! Also try it on a GitHub release ;)