[{"data":1,"prerenderedAt":1171},["ShallowReactive",2],{"content:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Frestoring-state-on-pageshow-after-bfcache":3,"surroundings:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Frestoring-state-on-pageshow-after-bfcache":1162},{"id":4,"title":5,"body":6,"description":1141,"extension":1142,"meta":1143,"navigation":1155,"path":1156,"seo":1157,"stem":1160,"__hash__":1161},"content\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Frestoring-state-on-pageshow-after-bfcache\u002Findex.md","Restoring State on pageshow After bfcache",{"type":7,"value":8,"toc":1123},"minimark",[9,14,29,41,180,185,221,225,231,237,243,252,387,391,396,541,544,548,647,650,654,657,735,739,745,749,756,882,886,893,897,907,911,943,947,953,963,969,975,979,1000,1004,1024,1042,1051,1060,1072,1081,1085,1108,1113,1116,1119],[10,11,13],"h1",{"id":12},"how-to-restore-and-refresh-state-on-pageshow-after-bfcache","How to Restore and Refresh State on pageshow After bfcache",[15,16,17,18,23,24,28],"p",{},"This guide completes the work in ",[19,20,22],"a",{"href":21},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002F","Back\u002FForward Cache (bfcache)",", within ",[19,25,27],{"href":26},"\u002Fadvanced-caching-strategies-cdn-architecture\u002F","Advanced Caching Strategies & CDN Architecture",". A page restored from bfcache resumes exactly as it was when the user left: the same DOM, the same JavaScript state, the same cart count and the same \"only 2 left\" stock badge. That is the point — it is instant — but some of that state may now be wrong. The user added an item in another tab; the price changed; the session expired; a countdown timer was paused while frozen.",[15,30,31,32,36,37,40],{},"The fix is to treat restoration as a resume event: listen for ",[33,34,35],"code",{},"pageshow"," with ",[33,38,39],{},"event.persisted === true",", and refresh exactly the state that can go stale. Done well, users get an instant page and correct data a moment later. Done badly — or not at all — teams sometimes \"fix\" stale-data bugs by making pages ineligible for bfcache, throwing away the performance win.",[15,42,43],{},[44,45,51,52,51,59,51,63,51,66,51,84,51,92,51,98,51,105,51,112,51,115,51,119,51,122,51,126,51,135,51,138,51,141,51,147,51,153,51,157,51,162,51,167,51,171,51,176,51],"svg",{"viewBox":46,"width":47,"role":48,"ariaLabel":49,"style":50},"0 0 760 290","100%","img","Sequence of a bfcache restoration followed by targeted refreshes of stale state.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[53,54],"rect",{"className":55,"x":57,"y":57,"width":47,"height":47,"fill":58},[56],"svg-canvas","0","#ffffff",[60,61,62],"title",{},"Restore, then refresh",[64,65,49],"desc",{},[67,68,69],"defs",{},[70,71,78],"marker",{"id":72,"viewBox":73,"refX":74,"refY":75,"markerWidth":76,"markerHeight":76,"orient":77},"fab801e5d9","0 0 10 10","9","5","7","auto-start-reverse",[79,80],"path",{"d":81,"fill":82,"style":83},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[53,85],{"x":86,"y":86,"width":87,"height":88,"rx":89,"fill":90,"stroke":82,"style":91},"1","758","288","10","none","stroke-opacity:0.18",[93,94,62],"text",{"x":95,"y":96,"fill":82,"style":97},"28.0","34.0","font-size:16px;font-weight:700",[53,99],{"x":100,"y":101,"width":102,"height":96,"rx":103,"fill":82,"stroke":82,"style":104},"70.3","56.0","150.0","6","fill-opacity:0.06;stroke-opacity:0.4",[93,106,111],{"x":107,"y":108,"fill":82,"style":109,"textAnchor":110},"145.3","78.0","font-size:13px;font-weight:700","middle","Browser",[53,113],{"x":114,"y":101,"width":102,"height":96,"rx":103,"fill":82,"stroke":82,"style":104},"305.0",[93,116,118],{"x":117,"y":108,"fill":82,"style":109,"textAnchor":110},"380.0","Page",[53,120],{"x":121,"y":101,"width":102,"height":96,"rx":103,"fill":82,"stroke":82,"style":104},"539.7",[93,123,125],{"x":124,"y":108,"fill":82,"style":109,"textAnchor":110},"614.7","API",[127,128],"line",{"x1":107,"y1":129,"x2":107,"y2":130,"stroke":82,"strokeWidth":131,"strokeDashArray":132,"style":134},"90.0","268.0","1.5",[133,133],"4","stroke-opacity:0.35",[127,136],{"x1":117,"y1":129,"x2":117,"y2":130,"stroke":82,"strokeWidth":131,"strokeDashArray":137,"style":134},[133,133],[127,139],{"x1":124,"y1":129,"x2":124,"y2":130,"stroke":82,"strokeWidth":131,"strokeDashArray":140,"style":134},[133,133],[127,142],{"x1":107,"y1":143,"x2":144,"y2":143,"stroke":82,"strokeWidth":131,"style":145,"markerEnd":146},"124.0","378.0","stroke-opacity:0.6","url(#fab801e5d9)",[93,148,152],{"x":149,"y":150,"fill":82,"style":151,"textAnchor":110},"262.7","117.0","font-size:12px","pageshow (persisted)",[127,154],{"x1":117,"y1":155,"x2":156,"y2":155,"stroke":82,"strokeWidth":131,"style":145,"markerEnd":146},"164.0","612.7",[93,158,161],{"x":159,"y":160,"fill":82,"style":151,"textAnchor":110},"497.3","157.0","GET \u002Fcart\u002Fsummary",[127,163],{"x1":124,"y1":164,"x2":165,"y2":164,"stroke":82,"strokeWidth":131,"strokeDashArray":166,"style":145,"markerEnd":146},"204.0","382.0",[75,133],[93,168,170],{"x":159,"y":169,"fill":82,"style":151,"textAnchor":110},"197.0","3 items",[127,172],{"x1":117,"y1":173,"x2":174,"y2":173,"stroke":82,"strokeWidth":131,"strokeDashArray":175,"style":145,"markerEnd":146},"244.0","147.3",[75,133],[93,177,179],{"x":149,"y":178,"fill":82,"style":151,"textAnchor":110},"237.0","badge updated",[181,182,184],"h2",{"id":183},"rapid-diagnosis","Rapid Diagnosis",[186,187,188,196,202,215],"ul",{},[189,190,191,195],"li",{},[192,193,194],"strong",{},"Navigate away and back"," on key templates after changing state elsewhere (add to cart in another tab, log out, wait past a timer). Note anything stale.",[189,197,198,201],{},[192,199,200],{},"Check timers and intervals."," They pause while frozen and may fire immediately on restore.",[189,203,204,207,208,211,212,214],{},[192,205,206],{},"Check connections."," WebSockets closed on ",[33,209,210],{},"pagehide"," must reopen on ",[33,213,35],{},".",[189,216,217,220],{},[192,218,219],{},"Check analytics."," Restored pages should register a view; some tools miss them.",[181,222,224],{"id":223},"root-cause-analysis","Root Cause Analysis",[15,226,227,230],{},[192,228,229],{},"1. Cross-tab changes."," State changed in another tab (cart, preferences, auth) is not reflected in the frozen page.",[15,232,233,236],{},[192,234,235],{},"2. Time-based state."," Countdown timers, \"time ago\" labels, and expiring offers show values from when the page was frozen.",[15,238,239,242],{},[192,240,241],{},"3. Server-side changes."," Prices, stock levels and notifications may have changed.",[15,244,245,248,249,251],{},[192,246,247],{},"4. Paused activity."," Polling and connections stopped on ",[33,250,210],{}," are not restarted.",[15,253,254],{},[44,255,51,258,51,261,51,264,51,266,51,269,51,271,51,275,51,282,51,286,51,290,51,293,51,297,51,300,51,304,51,309,51,312,51,314,51,317,51,320,51,324,51,326,51,329,51,331,51,334,51,337,51,341,51,343,51,346,51,348,51,351,51,354,51,358,51,362,51,365,51,367,51,370,51,373,51,377,51,379,51,382,51,384,51],{"viewBox":256,"width":47,"role":48,"ariaLabel":257,"style":50},"0 0 760 258","Categories of page state, whether they go stale while a page sits in bfcache, and how to refresh each.",[53,259],{"className":260,"x":57,"y":57,"width":47,"height":47,"fill":58},[56],[60,262,263],{},"What to refresh on restore",[64,265,257],{},[53,267],{"x":86,"y":86,"width":87,"height":268,"rx":89,"fill":90,"stroke":82,"style":91},"256",[93,270,263],{"x":95,"y":96,"fill":82,"style":97},[53,272],{"x":95,"y":101,"width":273,"height":274,"rx":57,"fill":82,"stroke":82,"style":104},"188.3","30.0",[93,276,281],{"x":277,"y":278,"fill":82,"style":279,"textAnchor":280},"38.0","75.5","font-size:12.5px;font-weight:700","start","State",[53,283],{"x":284,"y":101,"width":285,"height":274,"rx":57,"fill":82,"stroke":82,"style":104},"216.3","257.9",[93,287,289],{"x":288,"y":278,"fill":82,"style":279,"textAnchor":110},"345.2","Goes stale?",[53,291],{"x":292,"y":101,"width":285,"height":274,"rx":57,"fill":82,"stroke":82,"style":104},"474.1",[93,294,296],{"x":295,"y":278,"fill":82,"style":279,"textAnchor":110},"603.1","Refresh by",[53,298],{"x":95,"y":299,"width":273,"height":274,"rx":57,"fill":90,"stroke":82,"style":134},"86.0",[93,301,303],{"x":277,"y":302,"fill":82,"style":279,"textAnchor":280},"105.5","Cart count \u002F badge",[53,305],{"x":284,"y":299,"width":285,"height":274,"rx":57,"fill":306,"stroke":307,"style":308},"#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[93,310,311],{"x":288,"y":302,"fill":82,"style":151,"textAnchor":110},"yes, cross-tab",[53,313],{"x":292,"y":299,"width":285,"height":274,"rx":57,"fill":90,"stroke":82,"style":134},[93,315,316],{"x":295,"y":302,"fill":82,"style":151,"textAnchor":110},"fetch summary or BroadcastChannel",[53,318],{"x":95,"y":319,"width":273,"height":274,"rx":57,"fill":90,"stroke":82,"style":134},"116.0",[93,321,323],{"x":277,"y":322,"fill":82,"style":279,"textAnchor":280},"135.5","Prices and stock",[53,325],{"x":284,"y":319,"width":285,"height":274,"rx":57,"fill":306,"stroke":307,"style":308},[93,327,328],{"x":288,"y":322,"fill":82,"style":151,"textAnchor":110},"yes, server-side",[53,330],{"x":292,"y":319,"width":285,"height":274,"rx":57,"fill":90,"stroke":82,"style":134},[93,332,333],{"x":295,"y":322,"fill":82,"style":151,"textAnchor":110},"refetch visible items",[53,335],{"x":95,"y":336,"width":273,"height":274,"rx":57,"fill":90,"stroke":82,"style":134},"146.0",[93,338,340],{"x":277,"y":339,"fill":82,"style":279,"textAnchor":280},"165.5","Session \u002F auth",[53,342],{"x":284,"y":336,"width":285,"height":274,"rx":57,"fill":306,"stroke":307,"style":308},[93,344,345],{"x":288,"y":339,"fill":82,"style":151,"textAnchor":110},"yes",[53,347],{"x":292,"y":336,"width":285,"height":274,"rx":57,"fill":90,"stroke":82,"style":134},[93,349,350],{"x":295,"y":339,"fill":82,"style":151,"textAnchor":110},"check session endpoint",[53,352],{"x":95,"y":353,"width":273,"height":274,"rx":57,"fill":90,"stroke":82,"style":134},"176.0",[93,355,357],{"x":277,"y":356,"fill":82,"style":279,"textAnchor":280},"195.5","Scroll position and form input",[53,359],{"x":284,"y":353,"width":285,"height":274,"rx":57,"fill":360,"stroke":360,"style":361},"#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[93,363,364],{"x":288,"y":356,"fill":82,"style":151,"textAnchor":110},"no — keep it",[53,366],{"x":292,"y":353,"width":285,"height":274,"rx":57,"fill":90,"stroke":82,"style":134},[93,368,369],{"x":295,"y":356,"fill":82,"style":151,"textAnchor":110},"nothing",[53,371],{"x":95,"y":372,"width":273,"height":274,"rx":57,"fill":90,"stroke":82,"style":134},"206.0",[93,374,376],{"x":277,"y":375,"fill":82,"style":279,"textAnchor":280},"225.5","Timers and labels",[53,378],{"x":284,"y":372,"width":285,"height":274,"rx":57,"fill":82,"stroke":82,"style":104},[93,380,381],{"x":288,"y":375,"fill":82,"style":151,"textAnchor":110},"time-based",[53,383],{"x":292,"y":372,"width":285,"height":274,"rx":57,"fill":90,"stroke":82,"style":134},[93,385,386],{"x":295,"y":375,"fill":82,"style":151,"textAnchor":110},"recompute from timestamps",[181,388,390],{"id":389},"step-by-step-resolution","Step-by-Step Resolution",[392,393,395],"h3",{"id":394},"_1-centralise-restore-handling","1. Centralise restore handling",[397,398,403],"pre",{"className":399,"code":400,"language":401,"meta":402,"style":402},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","const onRestore = new Set();\nexport const whenRestored = (fn) => onRestore.add(fn);\naddEventListener('pageshow', (event) => {\n  if (event.persisted) for (const fn of onRestore) fn();\n});\n\u002F\u002F trade-off: a central registry makes it easy to add work on restore; keep each\n\u002F\u002F handler cheap and async so restoration stays instant.\n","javascript","",[33,404,405,431,467,493,522,528,535],{"__ignoreMap":402},[406,407,409,413,417,420,423,427],"span",{"class":127,"line":408},1,[406,410,412],{"class":411},"sPARh","const",[406,414,416],{"class":415},"sPXB4"," onRestore",[406,418,419],{"class":411}," =",[406,421,422],{"class":411}," new",[406,424,426],{"class":425},"smZ65"," Set",[406,428,430],{"class":429},"saISM","();\n",[406,432,434,437,440,443,445,448,452,455,458,461,464],{"class":127,"line":433},2,[406,435,436],{"class":411},"export",[406,438,439],{"class":411}," const",[406,441,442],{"class":425}," whenRestored",[406,444,419],{"class":411},[406,446,447],{"class":429}," (",[406,449,451],{"class":450},"sQw3B","fn",[406,453,454],{"class":429},") ",[406,456,457],{"class":411},"=>",[406,459,460],{"class":429}," onRestore.",[406,462,463],{"class":425},"add",[406,465,466],{"class":429},"(fn);\n",[406,468,470,473,476,480,483,486,488,490],{"class":127,"line":469},3,[406,471,472],{"class":425},"addEventListener",[406,474,475],{"class":429},"(",[406,477,479],{"class":478},"sZ8jY","'pageshow'",[406,481,482],{"class":429},", (",[406,484,485],{"class":450},"event",[406,487,454],{"class":429},[406,489,457],{"class":411},[406,491,492],{"class":429}," {\n",[406,494,496,499,502,505,507,509,512,515,518,520],{"class":127,"line":495},4,[406,497,498],{"class":411},"  if",[406,500,501],{"class":429}," (event.persisted) ",[406,503,504],{"class":411},"for",[406,506,447],{"class":429},[406,508,412],{"class":411},[406,510,511],{"class":415}," fn",[406,513,514],{"class":411}," of",[406,516,517],{"class":429}," onRestore) ",[406,519,451],{"class":425},[406,521,430],{"class":429},[406,523,525],{"class":127,"line":524},5,[406,526,527],{"class":429},"});\n",[406,529,531],{"class":127,"line":530},6,[406,532,534],{"class":533},"sjfSM","\u002F\u002F trade-off: a central registry makes it easy to add work on restore; keep each\n",[406,536,538],{"class":127,"line":537},7,[406,539,540],{"class":533},"\u002F\u002F handler cheap and async so restoration stays instant.\n",[15,542,543],{},"Expected outcome: features register their own refresh logic in one consistent way.",[392,545,547],{"id":546},"_2-refresh-cross-tab-and-server-state","2. Refresh cross-tab and server state",[397,549,551],{"className":399,"code":550,"language":401,"meta":402,"style":402},"whenRestored(async () => {\n  const summary = await fetch('\u002Fapi\u002Fcart\u002Fsummary', { cache: 'no-store' }).then((r) => r.json());\n  cartBadge.textContent = summary.count;\n});\n\u002F\u002F trade-off: a request per restore adds server load; for state that changes in\n\u002F\u002F other tabs only, BroadcastChannel or storage events can update without a request.\n",[33,552,553,570,622,633,637,642],{"__ignoreMap":402},[406,554,555,558,560,563,566,568],{"class":127,"line":408},[406,556,557],{"class":425},"whenRestored",[406,559,475],{"class":429},[406,561,562],{"class":411},"async",[406,564,565],{"class":429}," () ",[406,567,457],{"class":411},[406,569,492],{"class":429},[406,571,572,575,578,580,583,586,588,591,594,597,600,603,606,609,611,613,616,619],{"class":127,"line":433},[406,573,574],{"class":411},"  const",[406,576,577],{"class":415}," summary",[406,579,419],{"class":411},[406,581,582],{"class":411}," await",[406,584,585],{"class":425}," fetch",[406,587,475],{"class":429},[406,589,590],{"class":478},"'\u002Fapi\u002Fcart\u002Fsummary'",[406,592,593],{"class":429},", { cache: ",[406,595,596],{"class":478},"'no-store'",[406,598,599],{"class":429}," }).",[406,601,602],{"class":425},"then",[406,604,605],{"class":429},"((",[406,607,608],{"class":450},"r",[406,610,454],{"class":429},[406,612,457],{"class":411},[406,614,615],{"class":429}," r.",[406,617,618],{"class":425},"json",[406,620,621],{"class":429},"());\n",[406,623,624,627,630],{"class":127,"line":469},[406,625,626],{"class":429},"  cartBadge.textContent ",[406,628,629],{"class":411},"=",[406,631,632],{"class":429}," summary.count;\n",[406,634,635],{"class":127,"line":495},[406,636,527],{"class":429},[406,638,639],{"class":127,"line":524},[406,640,641],{"class":533},"\u002F\u002F trade-off: a request per restore adds server load; for state that changes in\n",[406,643,644],{"class":127,"line":530},[406,645,646],{"class":533},"\u002F\u002F other tabs only, BroadcastChannel or storage events can update without a request.\n",[15,648,649],{},"Expected outcome: badges and counts are correct within a few hundred milliseconds of restoration.",[392,651,653],{"id":652},"_3-recompute-time-based-values","3. Recompute time-based values",[15,655,656],{},"Store absolute timestamps and recompute labels and countdowns from them, rather than decrementing counters in intervals that pause while frozen.",[397,658,660],{"className":399,"code":659,"language":401,"meta":402,"style":402},"whenRestored(() => document.querySelectorAll('time[data-relative]').forEach((el) => {\n  el.textContent = formatRelative(new Date(el.dateTime));\n}));\n\u002F\u002F trade-off: recomputing many labels at once can cost a few milliseconds on\n\u002F\u002F long pages; limit it to elements in or near the viewport if needed.\n",[33,661,662,699,720,725,730],{"__ignoreMap":402},[406,663,664,666,669,671,674,677,679,682,685,688,690,693,695,697],{"class":127,"line":408},[406,665,557],{"class":425},[406,667,668],{"class":429},"(() ",[406,670,457],{"class":411},[406,672,673],{"class":429}," document.",[406,675,676],{"class":425},"querySelectorAll",[406,678,475],{"class":429},[406,680,681],{"class":478},"'time[data-relative]'",[406,683,684],{"class":429},").",[406,686,687],{"class":425},"forEach",[406,689,605],{"class":429},[406,691,692],{"class":450},"el",[406,694,454],{"class":429},[406,696,457],{"class":411},[406,698,492],{"class":429},[406,700,701,704,706,709,711,714,717],{"class":127,"line":433},[406,702,703],{"class":429},"  el.textContent ",[406,705,629],{"class":411},[406,707,708],{"class":425}," formatRelative",[406,710,475],{"class":429},[406,712,713],{"class":411},"new",[406,715,716],{"class":425}," Date",[406,718,719],{"class":429},"(el.dateTime));\n",[406,721,722],{"class":127,"line":469},[406,723,724],{"class":429},"}));\n",[406,726,727],{"class":127,"line":495},[406,728,729],{"class":533},"\u002F\u002F trade-off: recomputing many labels at once can cost a few milliseconds on\n",[406,731,732],{"class":127,"line":524},[406,733,734],{"class":533},"\u002F\u002F long pages; limit it to elements in or near the viewport if needed.\n",[392,736,738],{"id":737},"_4-re-establish-connections-and-polling","4. Re-establish connections and polling",[15,740,741,742,744],{},"Reopen WebSockets and restart polling that was stopped on ",[33,743,210],{},", then fetch any changes missed while frozen.",[392,746,748],{"id":747},"_5-handle-expired-sessions","5. Handle expired sessions",[15,750,751,752,214],{},"Check the session on restore for authenticated pages and redirect to login if it has ended — the pattern from ",[19,753,755],{"href":754},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Fcache-control-no-store-and-bfcache\u002F","Cache-Control no-store and bfcache",[15,757,758],{},[44,759,51,762,51,765,51,768,51,770,51,773,51,775,51,780,51,784,51,788,51,791,51,795,51,798,51,801,51,805,51,809,51,812,51,816,51,820,51,823,51,827,51,831,51,837,51,841,51,845,51,849,51,854,51,856,51,859,51,863,51,866,51,870,51,873,51,876,51,879,51],{"viewBox":760,"width":47,"role":48,"ariaLabel":761,"style":50},"0 0 760 381","Five things a page should do when restored from bfcache.",[53,763],{"className":764,"x":57,"y":57,"width":47,"height":47,"fill":58},[56],[60,766,767],{},"Restore handler checklist",[64,769,761],{},[53,771],{"x":86,"y":86,"width":87,"height":772,"rx":89,"fill":90,"stroke":82,"style":91},"379",[93,774,767],{"x":95,"y":96,"fill":82,"style":97},[53,776],{"x":777,"y":101,"width":778,"height":779,"rx":103,"fill":360,"stroke":360,"style":361},"72.0","660.0","51.0",[93,781,783],{"x":299,"y":782,"fill":82,"style":109,"textAnchor":280},"77.0","Keep scroll and form state",[93,785,787],{"x":299,"y":786,"fill":82,"style":151,"textAnchor":280},"94.0","That is the user's context — do not reset it",[53,789],{"x":777,"y":790,"width":778,"height":779,"rx":103,"fill":360,"stroke":360,"style":361},"119.0",[93,792,794],{"x":299,"y":793,"fill":82,"style":109,"textAnchor":280},"140.0","Refresh cross-tab state",[93,796,797],{"x":299,"y":160,"fill":82,"style":151,"textAnchor":280},"Cart, wishlist, notifications",[53,799],{"x":777,"y":800,"width":778,"height":779,"rx":103,"fill":360,"stroke":360,"style":361},"182.0",[93,802,804],{"x":299,"y":803,"fill":82,"style":109,"textAnchor":280},"203.0","Refetch volatile server data in view",[93,806,808],{"x":299,"y":807,"fill":82,"style":151,"textAnchor":280},"220.0","Prices and stock for visible items",[53,810],{"x":777,"y":811,"width":778,"height":779,"rx":103,"fill":360,"stroke":360,"style":361},"245.0",[93,813,815],{"x":299,"y":814,"fill":82,"style":109,"textAnchor":280},"266.0","Recompute time-based UI",[93,817,819],{"x":299,"y":818,"fill":82,"style":151,"textAnchor":280},"283.0","From stored timestamps",[53,821],{"x":777,"y":822,"width":778,"height":779,"rx":103,"fill":360,"stroke":360,"style":361},"308.0",[93,824,826],{"x":299,"y":825,"fill":82,"style":109,"textAnchor":280},"329.0","Reconnect and check session",[93,828,830],{"x":299,"y":829,"fill":82,"style":151,"textAnchor":280},"346.0","Sockets, polling, auth",[127,832],{"x1":833,"y1":834,"x2":833,"y2":835,"stroke":82,"strokeWidth":131,"style":836},"43.0","95.5","130.5","stroke-opacity:0.3",[127,838],{"x1":833,"y1":839,"x2":833,"y2":840,"stroke":82,"strokeWidth":131,"style":836},"158.5","193.5",[127,842],{"x1":833,"y1":843,"x2":833,"y2":844,"stroke":82,"strokeWidth":131,"style":836},"221.5","256.5",[127,846],{"x1":833,"y1":847,"x2":833,"y2":848,"stroke":82,"strokeWidth":131,"style":836},"284.5","319.5",[850,851],"circle",{"cx":833,"cy":852,"r":853,"fill":360},"81.5","13",[93,855,86],{"x":833,"y":299,"fill":58,"style":109,"textAnchor":110},[850,857],{"cx":833,"cy":858,"r":853,"fill":360},"144.5",[93,860,862],{"x":833,"y":861,"fill":58,"style":109,"textAnchor":110},"149.0","2",[850,864],{"cx":833,"cy":865,"r":853,"fill":360},"207.5",[93,867,869],{"x":833,"y":868,"fill":58,"style":109,"textAnchor":110},"212.0","3",[850,871],{"cx":833,"cy":872,"r":853,"fill":360},"270.5",[93,874,133],{"x":833,"y":875,"fill":58,"style":109,"textAnchor":110},"275.0",[850,877],{"cx":833,"cy":878,"r":853,"fill":360},"333.5",[93,880,75],{"x":833,"y":881,"fill":58,"style":109,"textAnchor":110},"338.0",[181,883,885],{"id":884},"verification","Verification",[15,887,888,889,892],{},"Script the scenario in an end-to-end test: open a product list, navigate to a product, add to cart in a second tab, go back in the first tab, and assert the badge updates. Confirm the restore remains instant (no full reload) by checking ",[33,890,891],{},"pageshow.persisted",". Monitor error rates for restore handlers in production.",[181,894,896],{"id":895},"worked-example-restoring-without-losing-instant-back","Worked Example: Restoring Without Losing Instant Back",[15,898,899,900,903,904,906],{},"An online grocery site had disabled bfcache with an ",[33,901,902],{},"unload"," handler after customers complained that the basket count was wrong after going back. The team removed the handler, added a restore registry, refreshed the basket summary and visible product availability on ",[33,905,35],{},", and recomputed delivery slot countdowns from timestamps. Back navigations became instant for 80% of users in Chromium, and complaints about stale baskets stopped because the count now updated within 200ms of returning.",[181,908,910],{"id":909},"common-mistakes","Common Mistakes",[186,912,913,925,931,937],{},[189,914,915,51,918,921,922,924],{},[192,916,917],{},"Reloading the page on restore.",[33,919,920],{},"location.reload()"," in ",[33,923,35],{}," throws away the benefit entirely.",[189,926,927,930],{},[192,928,929],{},"Refreshing everything."," Refetch only what can go stale; re-rendering the whole page negates the instant restore.",[189,932,933,936],{},[192,934,935],{},"Resetting scroll or form state."," Users expect to return to where they were.",[189,938,939,942],{},[192,940,941],{},"Blocking on refresh."," Show the restored page immediately; update asynchronously.",[181,944,946],{"id":945},"edge-cases","Edge Cases",[15,948,949,952],{},[192,950,951],{},"Optimistic UI."," Pending optimistic updates (an item marked \"adding…\") may have completed or failed while frozen; reconcile with server state on restore.",[15,954,955,958,959,962],{},[192,956,957],{},"Single-page apps with client caches."," Data libraries like TanStack Query can refetch on window focus; ensure that behaviour also triggers on restore, or call ",[33,960,961],{},"invalidateQueries"," from the restore handler.",[15,964,965,968],{},[192,966,967],{},"Analytics."," Send a page view on restore with the navigation type so restored views are counted.",[15,970,971,974],{},[192,972,973],{},"Feature flags."," If flags can change mid-session, decide whether a restored page should pick up new flags; usually it should not change layout under the user.",[181,976,978],{"id":977},"framework-integration","Framework Integration",[15,980,981,982,984,985,988,989,992,993,996,997,999],{},"Most frameworks do not handle restoration for you, but their data layers make it straightforward. In React, a small hook that subscribes to ",[33,983,35],{}," and calls your data library's refetch (",[33,986,987],{},"queryClient.invalidateQueries",", SWR's ",[33,990,991],{},"mutate",") refreshes visible queries. In Vue, a composable can do the same with Pinia stores or ",[33,994,995],{},"refreshNuxtData()"," in Nuxt. In both, keep the restore logic declarative: components declare which data is volatile, and a single listener refreshes those queries on restore, rather than every component adding its own ",[33,998,35],{}," handler. That keeps the restoration path cheap and predictable as the application grows.",[181,1001,1003],{"id":1002},"faq","FAQ",[1005,1006,1009,1013],"details",{"className":1007},[1008],"faq-item",[1010,1011,1012],"summary",{},"Does pageshow fire on normal page loads too?",[15,1014,1015,1016,1019,1020,1023],{},"Yes, with ",[33,1017,1018],{},"persisted"," false. Check ",[33,1021,1022],{},"event.persisted"," to run restore-only logic.",[1005,1025,1027,1030],{"className":1026},[1008],[1010,1028,1029],{},"Is visibilitychange enough instead of pageshow?",[15,1031,1032,1035,1036,1038,1039,1041],{},[33,1033,1034],{},"visibilitychange"," fires on tab switches too, which is useful for refreshing data when a user returns to a tab, but it does not tell you the page was restored from bfcache. Use ",[33,1037,35],{}," for restore-specific logic and ",[33,1040,1034],{}," for general freshness.",[1005,1043,1045,1048],{"className":1044},[1008],[1010,1046,1047],{},"Will timers fire immediately after restore?",[15,1049,1050],{},"Timers pause while frozen; when the page resumes, any timer whose time has passed may fire promptly. Code that assumes regular intervals should compute from timestamps rather than counting ticks.",[1005,1052,1054,1057],{"className":1053},[1008],[1010,1055,1056],{},"How quickly should refreshed data appear?",[15,1058,1059],{},"Within a few hundred milliseconds is typical and acceptable; the page itself is already visible and interactive. Indicate pending refreshes subtly for important values like prices.",[1005,1061,1063,1066],{"className":1062},[1008],[1010,1064,1065],{},"Can BroadcastChannel keep tabs in sync instead?",[15,1067,1068,1069,1071],{},"Yes, for cross-tab changes: a tab that adds to the cart can broadcast the new count, and other tabs update on receipt or on restore. Close channels on ",[33,1070,210],{}," if your browser targets treat open channels as blockers.",[1005,1073,1075,1078],{"className":1074},[1008],[1010,1076,1077],{},"Should restored pages re-run A\u002FB test assignment?",[15,1079,1080],{},"No. The user already saw a variant; changing it on restore would alter the page under them and corrupt experiment data. Keep the original assignment and only refresh data that can legitimately change, such as prices or counts.",[181,1082,1084],{"id":1083},"related","Related",[186,1086,1087,1094,1101],{},[189,1088,1089,1093],{},[19,1090,1092],{"href":1091},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Fmeasuring-bfcache-hit-rate-in-rum\u002F","Measuring bfcache hit rate in RUM"," — confirming restores happen.",[189,1095,1096,1100],{},[19,1097,1099],{"href":1098},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fstale-while-revalidate-data-fetching-with-swr-and-tanstack-query\u002F","Stale-while-revalidate data fetching with SWR and TanStack Query"," — the data-layer equivalent of restore-then-refresh.",[189,1102,1103,1107],{},[19,1104,1106],{"href":1105},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Ffixing-bfcache-eligibility-blockers\u002F","Fixing bfcache eligibility blockers"," — making restoration possible first.",[1109,1110,1112],"script",{"type":1111},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Restore and Refresh State on pageshow After bfcache\",\n  \"description\": \"Patterns for refreshing time-sensitive data, timers and connections when a page is restored from the back\u002Fforward cache.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Centralise restore handling\",\n      \"text\": \"Expected outcome: features register their own refresh logic in one consistent way.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Refresh cross-tab and server state\",\n      \"text\": \"Expected outcome: badges and counts are correct within a few hundred milliseconds of restoration.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Recompute time-based values\",\n      \"text\": \"Store absolute timestamps and recompute labels and countdowns from them, rather than decrementing counters in intervals that pause while frozen.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Re-establish connections and polling\",\n      \"text\": \"Reopen WebSockets and restart polling that was stopped on pagehide, then fetch any changes missed while frozen.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 5,\n      \"name\": \"Handle expired sessions\",\n      \"text\": \"Check the session on restore for authenticated pages and redirect to login if it has ended — the pattern from Cache-Control no-store and bfcache.\"\n    }\n  ]\n}\n",[1109,1114,1115],{"type":1111},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Restore and Refresh State on pageshow After bfcache\",\n  \"description\": \"Patterns for refreshing time-sensitive data, timers and connections when a page is restored from the back\u002Fforward cache.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Frestoring-state-on-pageshow-after-bfcache\u002F\"\n  }\n}\n",[1109,1117,1118],{"type":1111},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Advanced Caching Strategies & CDN Architecture\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Back\u002FForward Cache (bfcache)\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Restoring State on pageshow After bfcache\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Frestoring-state-on-pageshow-after-bfcache\u002F\"\n    }\n  ]\n}\n",[1120,1121,1122],"style",{},"html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":402,"searchDepth":433,"depth":433,"links":1124},[1125,1126,1127,1134,1135,1136,1137,1138,1139,1140],{"id":183,"depth":433,"text":184},{"id":223,"depth":433,"text":224},{"id":389,"depth":433,"text":390,"children":1128},[1129,1130,1131,1132,1133],{"id":394,"depth":469,"text":395},{"id":546,"depth":469,"text":547},{"id":652,"depth":469,"text":653},{"id":737,"depth":469,"text":738},{"id":747,"depth":469,"text":748},{"id":884,"depth":433,"text":885},{"id":895,"depth":433,"text":896},{"id":909,"depth":433,"text":910},{"id":945,"depth":433,"text":946},{"id":977,"depth":433,"text":978},{"id":1002,"depth":433,"text":1003},{"id":1083,"depth":433,"text":1084},"Patterns for refreshing time-sensitive data, timers and connections when a page is restored from the back\u002Fforward cache.","md",{"slug":1144,"type":1145,"breadcrumb":1146,"datePublished":1154,"dateModified":1154},"restoring-state-on-pageshow-after-bfcache","article",[1147,1150,1151,1152],{"name":1148,"url":1149},"Home","\u002F",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1153},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Frestoring-state-on-pageshow-after-bfcache\u002F","2026-10-06",true,"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Frestoring-state-on-pageshow-after-bfcache",{"title":1158,"description":1159},"Restoring State on pageshow After a bfcache Restore","Pages restored from bfcache resume with old state. Refresh carts, prices, sessions and timers on pageshow so instant back navigations stay correct.","advanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Frestoring-state-on-pageshow-after-bfcache\u002Findex","Xs-n7n-5vKjV6PKPClp1B9Wh2og2Ir-AdwXvsQKrDHM",[1163,1167],{"title":1164,"path":1165,"stem":1166},"Replacing unload Handlers for bfcache","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Freplacing-unload-handlers-for-bfcache","advanced-caching-strategies-cdn-architecture\u002Fback-forward-cache-bfcache\u002Freplacing-unload-handlers-for-bfcache\u002Findex",{"title":1168,"path":1169,"stem":1170},"Cache Invalidation Patterns","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns","advanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Findex",1791308075032]