Skip to content

Commit 6db4a60

Browse files
committed
fix(router): fix spa navigation between pages in hybrid mode
1 parent ac6b8dd commit 6db4a60

File tree

3 files changed

+24
-27
lines changed

3 files changed

+24
-27
lines changed

packages/router/src/index.ts

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -372,41 +372,38 @@ export function router(): RouterBuilder {
372372
// any morph (even with identical HTML) would replace the live DOM nodes,
373373
// destroying the event listeners and signal bindings ilha.mount() wired up.
374374
//
375-
// Instead we mount a tiny "sentinel" island that subscribes to activeIsland.
376-
// On its first render (now) it sees the current value and returns empty markup.
377-
// When activeIsland changes (= real navigation), the sentinel unmounts itself,
378-
// and mounts RouterView onto [data-router-view] (or host as fallback) so the
379-
// router takes over rendering from that point on.
375+
// Instead we mount a "navigation handler" island that subscribes to activeIsland.
376+
// It tracks the current island and re-renders with hydration whenever the route changes.
380377
const viewHost = host.querySelector<Element>("[data-router-view]") ?? host;
381-
const initialIsland = activeIsland();
378+
let currentMountedIsland: Island<any, any> | null = activeIsland();
382379

383-
const sentinel = ilha.render((): string => {
380+
const navHandler = ilha.render((): string => {
384381
const current = activeIsland();
385-
if (current !== initialIsland) {
382+
if (current !== currentMountedIsland) {
386383
// activeIsland changed — a navigation happened.
387-
// Schedule RouterView takeover after this render completes.
384+
// Schedule re-hydration after this render completes.
388385
queueMicrotask(async () => {
389-
unmountSentinel();
390-
sentinelHost.remove();
386+
// Clean up previous view
387+
unmountView?.();
391388
// Mount the new route with hydration if registry is available
392389
unmountView = await mountRouteWithHydration(current, viewHost, registry);
390+
currentMountedIsland = current;
393391
});
394392
}
395-
// Sentinel itself produces no visible markup — the SSR content is
396-
// already in the DOM and we must not touch it.
393+
// Navigation handler produces no visible markup — it just tracks route changes.
397394
return "";
398395
});
399396

400-
// Mount sentinel on a hidden helper node so it doesn't interfere with
397+
// Mount nav handler on a hidden helper node so it doesn't interfere with
401398
// the existing [data-router-view] children.
402-
const sentinelHost = document.createElement("div");
403-
sentinelHost.style.display = "none";
404-
host.appendChild(sentinelHost);
405-
const unmountSentinel = sentinel.mount(sentinelHost);
399+
const navHost = document.createElement("div");
400+
navHost.style.display = "none";
401+
host.appendChild(navHost);
402+
const unmountNavHandler = navHandler.mount(navHost);
406403

407404
return () => {
408-
unmountSentinel();
409-
sentinelHost.remove();
405+
unmountNavHandler();
406+
navHost.remove();
410407
unmountView?.();
411408
_popstateCleanup?.();
412409
_linkCleanup?.();

templates/nitro/bun.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

templates/nitro/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"preview": "vite preview"
1010
},
1111
"dependencies": {
12-
"@ilha/router": "https://pkg.pr.new/ilhajs/ilha/@ilha/router@033add5",
13-
"ilha": "https://pkg.pr.new/ilhajs/ilha@033add5",
12+
"@ilha/router": "https://pkg.pr.new/ilhajs/ilha/@ilha/router@ac6b8dd",
13+
"ilha": "https://pkg.pr.new/ilhajs/ilha@ac6b8dd",
1414
"nitro": "^3.0.260311-beta"
1515
},
1616
"devDependencies": {

0 commit comments

Comments
 (0)