From ca077b429bf0f36a4ef04603a9baf23e05d7359d Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 16 Apr 2026 13:35:45 +0000 Subject: [PATCH] hydrate-island: clear-and-replace instead of hydration walk Islands now clear SSR children before render-to-dom and append the fresh DOM result. Avoids hydrate-mismatch errors from SSR/client attribute differences (~tw generates different class strings). The hydrating scope is set to nil (no cursor walk) so render-to-dom creates new DOM nodes instead of trying to reuse SSR elements. Co-Authored-By: Claude Opus 4.6 (1M context) --- shared/static/wasm/sx/boot.sx | 11 ++++++++--- web/boot.sx | 11 ++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/shared/static/wasm/sx/boot.sx b/shared/static/wasm/sx/boot.sx index a5b319d1..6cb0f604 100644 --- a/shared/static/wasm/sx/boot.sx +++ b/shared/static/wasm/sx/boot.sx @@ -332,20 +332,25 @@ (component-params comp)) (let ((cursor (dict "parent" el "index" 0))) - (scope-push! "sx-hydrating" cursor) + (host-call el "replaceChildren") + (scope-push! "sx-hydrating" nil) (cek-try (fn () (with-island-scope (fn (disposable) (append! disposers disposable)) - (fn () (render-to-dom (component-body comp) local nil)))) + (fn + () + (let + ((result (render-to-dom (component-body comp) local el))) + (when result (dom-append el result)))))) (fn (err) (scope-pop! "sx-hydrating") (log-warn (str "hydrate fallback: " comp-name " — " err)) (let - ((fallback (cek-try (fn () (with-island-scope (fn (d) (append! disposers d)) (fn () (render-to-dom (component-body comp) local nil)))) (fn (err2) (let ((e (dom-create-element "div" nil))) (dom-set-text-content e (str "Island error: " comp-name "\n" err2)) e))))) + ((fallback (dom-create-text (str "Island error: " comp-name " " err)))) (host-call el "replaceChildren" fallback) nil))) (scope-pop! "sx-hydrating") diff --git a/web/boot.sx b/web/boot.sx index a5b319d1..6cb0f604 100644 --- a/web/boot.sx +++ b/web/boot.sx @@ -332,20 +332,25 @@ (component-params comp)) (let ((cursor (dict "parent" el "index" 0))) - (scope-push! "sx-hydrating" cursor) + (host-call el "replaceChildren") + (scope-push! "sx-hydrating" nil) (cek-try (fn () (with-island-scope (fn (disposable) (append! disposers disposable)) - (fn () (render-to-dom (component-body comp) local nil)))) + (fn + () + (let + ((result (render-to-dom (component-body comp) local el))) + (when result (dom-append el result)))))) (fn (err) (scope-pop! "sx-hydrating") (log-warn (str "hydrate fallback: " comp-name " — " err)) (let - ((fallback (cek-try (fn () (with-island-scope (fn (d) (append! disposers d)) (fn () (render-to-dom (component-body comp) local nil)))) (fn (err2) (let ((e (dom-create-element "div" nil))) (dom-set-text-content e (str "Island error: " comp-name "\n" err2)) e))))) + ((fallback (dom-create-text (str "Island error: " comp-name " " err)))) (host-call el "replaceChildren" fallback) nil))) (scope-pop! "sx-hydrating")