From ceb391db249c91e6d0aac94dd00eaefe8803cc3a Mon Sep 17 00:00:00 2001 From: Steve Simonson <144349219+SteveSimonson@users.noreply.github.com> Date: Sun, 2 Aug 2026 11:10:39 -0500 Subject: [PATCH 1/3] Reload stale pages restored from bfcache --- src/main.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.tsx b/src/main.tsx index bef5202..6fbaa53 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -3,6 +3,14 @@ import { createRoot } from 'react-dom/client' import './index.css' import App from './App.tsx' +// A tab restored from the browser back/forward cache can contain an old React +// tree and old hashed assets even though the HTML shell is configured to +// revalidate. Reload persisted pages so returning visitors see the current +// layout and imagery instead of a stale pre-deploy render. +window.addEventListener('pageshow', (event) => { + if (event.persisted) window.location.reload() +}) + createRoot(document.getElementById('root')!).render( From fa2c97afba6d1fb66791f7c1ed53da69b8d900ac Mon Sep 17 00:00:00 2001 From: Steve Simonson <144349219+SteveSimonson@users.noreply.github.com> Date: Sun, 2 Aug 2026 11:17:15 -0500 Subject: [PATCH 2/3] Reload only when the storefront bundle changed --- src/main.tsx | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index 6fbaa53..9e688aa 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,10 +5,43 @@ import App from './App.tsx' // A tab restored from the browser back/forward cache can contain an old React // tree and old hashed assets even though the HTML shell is configured to -// revalidate. Reload persisted pages so returning visitors see the current -// layout and imagery instead of a stale pre-deploy render. +// revalidate. Compare the active bundle with the latest shell and only reload +// when a deployment actually changed, preserving scroll/form state otherwise. +async function refreshPersistedPageIfStale(event: PageTransitionEvent) { + if (!event.persisted) return + + const activeScript = [...document.scripts].find((script) => + /\/assets\/index-[^/]+\.js$/.test(script.src), + ) + if (!activeScript) return + + try { + const checkUrl = new URL(window.location.href) + checkUrl.searchParams.set('__bfcache_check', String(Date.now())) + checkUrl.hash = '' + const response = await fetch(checkUrl, { + cache: 'no-store', + headers: { Accept: 'text/html' }, + }) + if (!response.ok) return + + const latestHtml = await response.text() + const latestDocument = new DOMParser().parseFromString(latestHtml, 'text/html') + const latestScript = [...latestDocument.scripts].find((script) => + /\/assets\/index-[^/]+\.js$/.test(script.src), + ) + if (!latestScript) return + + const activePath = new URL(activeScript.src, window.location.href).pathname + const latestPath = new URL(latestScript.src, window.location.href).pathname + if (activePath !== latestPath) window.location.reload() + } catch { + // Keep the restored page usable when the network is unavailable. + } +} + window.addEventListener('pageshow', (event) => { - if (event.persisted) window.location.reload() + void refreshPersistedPageIfStale(event) }) createRoot(document.getElementById('root')!).render( From ec6fe927fc7212dc191be30db63b66684d0ff007 Mon Sep 17 00:00:00 2001 From: Steve Simonson <144349219+SteveSimonson@users.noreply.github.com> Date: Sun, 2 Aug 2026 11:20:00 -0500 Subject: [PATCH 3/3] Compare all hashed assets before bfcache refresh --- src/main.tsx | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index 9e688aa..66ddea7 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,15 +5,21 @@ import App from './App.tsx' // A tab restored from the browser back/forward cache can contain an old React // tree and old hashed assets even though the HTML shell is configured to -// revalidate. Compare the active bundle with the latest shell and only reload -// when a deployment actually changed, preserving scroll/form state otherwise. +// revalidate. Compare the active hashed assets with the latest shell and only +// reload when a deployment actually changed, preserving state otherwise. +function hashedAssetPaths(root: Document) { + return [...root.querySelectorAll('script[src], link[href]')] + .map((element) => element.getAttribute('src') || element.getAttribute('href')) + .filter((asset): asset is string => Boolean(asset && asset.includes('/assets/'))) + .map((asset) => new URL(asset, window.location.href).pathname) + .sort() +} + async function refreshPersistedPageIfStale(event: PageTransitionEvent) { if (!event.persisted) return - const activeScript = [...document.scripts].find((script) => - /\/assets\/index-[^/]+\.js$/.test(script.src), - ) - if (!activeScript) return + const activeAssets = hashedAssetPaths(document) + if (!activeAssets.length) return try { const checkUrl = new URL(window.location.href) @@ -27,14 +33,10 @@ async function refreshPersistedPageIfStale(event: PageTransitionEvent) { const latestHtml = await response.text() const latestDocument = new DOMParser().parseFromString(latestHtml, 'text/html') - const latestScript = [...latestDocument.scripts].find((script) => - /\/assets\/index-[^/]+\.js$/.test(script.src), - ) - if (!latestScript) return - - const activePath = new URL(activeScript.src, window.location.href).pathname - const latestPath = new URL(latestScript.src, window.location.href).pathname - if (activePath !== latestPath) window.location.reload() + const latestAssets = hashedAssetPaths(latestDocument) + if (latestAssets.length && JSON.stringify(activeAssets) !== JSON.stringify(latestAssets)) { + window.location.reload() + } } catch { // Keep the restored page usable when the network is unavailable. }