Skip to content

Commit 09c06e3

Browse files
committed
fix(router): forward streamed Suspense resolvers on same-layout nav
Streamed Suspense templates (<template data-webjs-resolve="...">) are emitted at body level, AFTER the </div> that closes the data-layout wrapper. During same-layout navigation the router swaps only the <main> contents inside the wrapper, so those resolvers were dropped and the fallback stayed frozen ("computing timestamp…" forever). Forward them explicitly: after the swap, query the fetched body for template[data-webjs-resolve] and append clones onto the live body. The suspense boot's MutationObserver picks them up automatically.
1 parent 4915290 commit 09c06e3

1 file changed

Lines changed: 19 additions & 0 deletions

File tree

packages/core/src/router-client.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ async function performNavigation(href, isPopState) {
221221
(n) => !(n instanceof HTMLTemplateElement && /** @type any */ (n).getAttribute('shadowrootmode'))
222222
);
223223
swapSlotContent(target, children);
224+
// Forward any streamed Suspense resolvers that sit outside the layout
225+
// wrapper (they're emitted at body level, after </div>). Without this,
226+
// the new page's fallback boundary never gets its deferred content.
227+
forwardSuspenseResolvers(doc.body);
224228
} else {
225229
// Different layout structure — full swap.
226230
// Move nodes directly from the parsed doc (preserves DSD shadow roots)
@@ -434,6 +438,21 @@ function upgradeTree(root) {
434438
}
435439
}
436440

441+
/**
442+
* Copy streamed Suspense resolver templates + scripts from the fetched
443+
* document body onto the live document body. The `<template>` elements carry
444+
* a `data-webjs-resolve="<id>"` attribute; the suspense boot's
445+
* MutationObserver watches for them and replaces the matching
446+
* `<webjs-boundary id="<id>">` with the template content.
447+
*
448+
* @param {HTMLElement} fetchedBody
449+
*/
450+
function forwardSuspenseResolvers(fetchedBody) {
451+
for (const tpl of fetchedBody.querySelectorAll('template[data-webjs-resolve]')) {
452+
document.body.appendChild(tpl.cloneNode(true));
453+
}
454+
}
455+
437456
/** Re-run `<script>` tags in a container (innerHTML doesn't execute them). */
438457
function reactivateScripts(container) {
439458
for (const old of container.querySelectorAll('script')) {

0 commit comments

Comments
 (0)