Upgrade to Pro — share decks privately, control downloads, hide ads and more …

🇬🇧 iJS London 2026

🇬🇧 iJS London 2026

Web Performance APIs That You (Probably) Never Knew Existed

Responsiveness to user interaction is crucial for modern web apps, and we’ve all heard about many fantastic tools for measuring and optimizing performance.

However, we no longer have to rely entirely on pre-built dashboards to understand performance. Browsers already expose a rich set of native performance APIs that let us go deeper.

In this talk, we’ll explore lesser-known platform primitives for measuring responsiveness and diagnosing bottlenecks. From observers and timing APIs to scheduling primitives and resource hints, through navigation improvements like View Transitions.

Be ready to turn your browser into a performance toolkit and build performance metrics that reflect what your users really care about.

Avatar for Matheus Albuquerque

Matheus Albuquerque PRO

May 12, 2026

More Decks by Matheus Albuquerque

Transcript

  1. WEB PERFORMANCE APIS THAT YOU (PROBABLY) NEVER KNEW EXISTED •

    MAY 12, 2026. Hello, London! 👋 🇬🇧
  2. Matheus Albuquerque ↝ 👨💻 STAFF SWE @ MEDALLIA ↝ ⚛

    CHAIR @ REACT SUMMIT NYC ↝ ⚡ GOOGLE DEVELOPER EXPERT ↝ 𝕏 YTHECOMBINATOR
  3. Å

  4. ↝ PREAMBLE ↝ MEASURING WITH THE PLATFORM ↝ OPTIMIZING WITH

    THE PLATFORM ↝ WHY IT MATTERS ↝ CLOSING THOUGHTS THIS TALK PRESENTS…
  5. MEASURING WITH THE PLATFORM GENERAL STUFF VISIBILITY STATE • INTERSECTION

    OBSERVER • LAYOUT INSTABILITY PROFILING LONG TASKS • LONG ANIMATION FRAMES • SELF PROFILING • MEMORY USAGE TIMING USER TIMING • EVENT TIMING • RESOURCE TIMING • SERVER TIMING • ELEMENT TIMING SENSORS NETWORK STATUS • BATTERY STATUS • COMPUTE PRESSURE
  6. PAGE VISIBILITY: OVERVIEW document.hidden; document.visibilityState; document.addEventListener("visibilitychange", () = > {

    if (document.visibilityState===“hidden") { console.log("This window is hidden") } else { console.log("This window is visible") } })
  7. ↝ CANCEL POLLING LOOPS, TIMERS, AND BACKGROUND NETWORK REFRESHES WHEN

    THE TAB BECOMES HIDDEN. ↝ PAUSE VIDEO, AUDIO, AND ANIMATION LOOPS WHEN THE USER ISN’T ACTIVELY WATCHING THE PAGE. ↝ RESUME PROGRESSIVE DOWNLOADS OR DYNAMIC IMPORTS WHEN THE PAGE BECOMES VISIBLE AGAIN. PAGE VISIBILITY: IDEAS
  8. ↝ STOP REFRESHING DASHBOARDS OR LIVE DATA FEEDS WHILE THE

    TAB IS BACKGROUNDED. ↝ SEND ANALYTICS RELIABLY AT SESSION END WHEN VISIBILITY BECOMES HIDDEN. ↝ SILENCE NOTIFICATIONS OR SOUNDS WHEN THE DEVICE SCREEN LOCKS OR THE TAB LOSES VISIBILITY. PAGE VISIBILITY: IDEAS
  9. INTERSECTION OBSERVER: OVERVIEW const onIntersection = (entries) = > {

    for (const entry of entries) { if (entry.isIntersecting) { console.log(entry); } } }; const observer = new IntersectionObserver(onIntersection); observer.observe(document.querySelector('#target'));
  10. ↝ INITIALIZE EXPENSIVE RENDERING PIPELINES (CHARTS, CANVAS, WEBGL) ONLY WHEN

    NEEDED. ↝ LAZY-LOAD IMAGES, IFRAMES, AND ROUTE BUNDLES WHEN THEY APPROACH THE VIEWPORT. ↝ DELAY LOADING THIRD-PARTY WIDGETS (MAPS, VIDEO EMBEDS, CHAT CLIENTS) UNTIL VISIBLE. ↝ CONTROL MEDIA PLAYBACK DEPENDING ON VISIBILITY. INTERSECTION OBSERVER: IDEAS
  11. ↝ RENDER/HYDRATE COMPLEX UI COMPONENTS ONLY WHEN THEY BECOME RELEVANT.

    ↝ TRIGGER ANALYTICS EVENTS WHEN USERS ACTUALLY SEE IMPORTANT UI SECTIONS. ↝ MEASURE HOW LONG ELEMENTS REMAIN VISIBLE (E.G., AD IMPRESSIONS). INTERSECTION OBSERVER: IDEAS
  12. ↝ MANY WEBSITES HAVE DOM ELEMENTS SHIFTING AROUND DUE TO

    CONTENT LOADING ASYNCHRONOUSLY. ↝ MEASURE REAL CLS VALUES IN PRODUCTION. ↝ IDENTIFY THE EXACT DOM ELEMENTS RESPONSIBLE FOR LAYOUT INSTABILITY. ↝ IGNORE EXPECTED SHIFTS CAUSED BY USER INTERACTION. LAYOUT INSTABILITY: OVERVIEW
  13. LAYOUT INSTABILITY: OVERVIEW let cls = 0; new PerformanceObserver((list) =

    > { for (const entry of list.getEntries()) { if (entry.hadRecentInput) continue; / / ignore expected shifts cls += entry.value; const culprit = entry.sources?.[0]?.node; console.log("Shift:", entry.value); console.log("Element:", culprit); console.log("CLS so far:", cls); } }).observe({ type: "layout-shift", buffered: true });
  14. ↝ DETECT INSTABILITY INTRODUCED BY THIRD-PARTY EMBEDS OR PERSONALIZATION. ↝

    DETECT IMAGES OR IFRAMES LOADING WITHOUT A RESERVED SPACE. ↝ DETECT LAYOUT SHIFTS CAUSED BY WEB-FONT SWAPS. LAYOUT INSTABILITY: IDEAS
  15. ↝ MEASURE ROUTE TRANSITIONS IN AN SPA. ↝ MEASURE API

    LATENCY (REQUEST → RENDER). ↝ MEASURE THE TIME BETWEEN USER INTENT AND UI CONFIRMATION. ↝ MEASURE HYDRATION FOR ISLANDS/COMPONENTS. ↝ TRACK TIME SPENT INSIDE SUSPENSE FALLBACKS. USER TIMING: IDEAS
  16. EVENT TIMING: OVERVIEW new PerformanceObserver(list = > { for (const

    entry of list.getEntries()) { console.log("Input delay:", entry.processingStart - entry.startTime); console.log("Handler time:", entry.processingEnd - entry.processingStart); console.log("Total latency:", entry.duration); } }).observe({ type: "event", buffered: true });
  17. ↝ MEASURE DROPDOWN RESPONSIVENESS. ↝ MEASURE DRAG-AND-DROP LATENCY. ↝ DETECT

    SLOW SLIDER INTERACTIONS. ↝ DETECT INPUT LAG IN FORMS. ↝ TRACK RESPONSIVENESS REGRESSIONS AFTER RELEASES. EVENT TIMING: IDEAS
  18. RESOURCE TIMING: OVERVIEW new PerformanceObserver(list = > { for (const

    entry of list.getEntries()) { console.log(entry.name); console.log("TTFB:", entry.responseStart - entry.requestStart); console.log("Download:", entry.responseEnd - entry.responseStart); console.log("Cache Hit:" entry.transferSize === 0); } }).observe({ type: "resource", buffered: true });
  19. ↝ MEASURE THIRD-PARTY SCRIPTS COST. ↝ MEASURE API ENDPOINT LATENCY.

    ↝ DETECT SLOW FONT LOADING. ↝ DETECT CACHE MISSES VS CACHE HITS. ↝ DETECT CDN CONFIGURATION ISSUES. RESOURCE TIMING: IDEAS
  20. SERVER TIMING: OVERVIEW Server-Timing: database;dur=123;desc="Database", templating;dur=56;desc="Template processing" const nav =

    performance.getEntriesByType("navigation")[0]; console.log(nav.serverTiming); SERVER CLIENT
  21. ↝ MEASURE DB LATENCY IN THE BROWSER. ↝ DETECT A

    SLOW AUTH MIDDLEWARE. ↝ DEBUG EDGE-CACHE EFFECTIVENESS. ↝ DETECT TEMPLATE RENDERING BOTTLENECKS. SERVER TIMING: IDEAS
  22. ↝ ALLOWS YOU TO MEASURE THE RENDER TIME OF SPECIFIC

    ELEMENTS. ↝ USEFUL FOR KNOWING WHEN THE LARGEST IMAGE OR TEXT BLOCK WAS PAINTED TO THE SCREEN. ↝ THE BASIS FOR THE LARGEST CONTENTFUL PAINT (LCP) METRIC. ELEMENT TIMING: OVERVIEW
  23. ELEMENT TIMING: OVERVIEW <img src="/hero.jpg" elementtiming="hero" /> <script> new PerformanceObserver(list

    = > { const entry = list.getEntries()[0]; console.log("Hero render:", entry.startTime); }).observe({ type: "element", buffered: true }); </script>
  24. ↝ MEASURE HERO IMAGE RENDER TIMING. ↝ MEASURE ABOVE-THE-FOLD READINESS.

    ↝ TRACK MARKETING CTA VISIBILITY TIMING. ↝ MEASURE WHEN SKELETON LOADERS DISAPPEAR. ↝ REPLACE SYNTHETIC LCP TRACKING WITH PRODUCT- SPECIFIC METRICS. ELEMENT TIMING: IDEAS
  25. ↝ DISABLE ROUTE PREFETCHING ON SLOW CONNECTIONS. ↝ SKIP ANALYTICS

    UPLOADS WHEN RTT IS HIGH. ↝ LOAD LOWER-RESOLUTION ON CONSTRAINED BANDWIDTH. ↝ ADAPT TO SITUATIONS WHEN USERS ARE OFFLINE. ↝ RESPECT DATA SAVING AS A USER PERFORMANCE PREFERENCE SIGNAL. NETWORK STATUS: IDEAS
  26. NETWORK STATUS: IDEAS switch (connectionType) { case "4g": return <Video

    src={videoSrc} />; case "3g": return <Image src={imageSrc.hiRes} alt={alt} />; default: return <Image src={imageSrc.lowRes} alt={alt} />; }
  27. BATTERY STATUS: OVERVIEW const batteryInfo = await navigator.getBattery(); batteryInfo.addEventListener("chargingchange", listener);

    batteryInfo.addEventListener("chargingtimechange", listener); batteryInfo.addEventListener("dischargingtimechange", listener); batteryInfo.addEventListener("levelchange", listener);
  28. ↝ DISABLE BACKGROUND POLLING ON LOW BATTERY. ↝ REDUCE ANIMATION

    FREQUENCY. ↝ STOP PREFETCHING BUNDLES. ↝ PERSIST UNSAVED DATA BEFORE SHUTDOWN RISK. ↝ SWITCH TO A STATIC MAP RATHER THAN AN INTERACTIVE ONE. BATTERY STATUS: IDEAS
  29. COMPUTE PRESSURE: OVERVIEW const observer = new PressureObserver(records = >

    { const state = records[0].state; if (state === "critical") { reduceRenderingQuality(); } }); observer.observe("cpu", { sampleInterval: 2000 });
  30. ↝ LOWER CANVAS RESOLUTION DYNAMICALLY. ↝ PAUSE BACKGROUND DATA PROCESSING

    PIPELINES. ↝ SWITCH FROM REAL-TIME UPDATES TO BATCHED ONES. ↝ DROP FRAME RATE IN WEBGL SCENES. ↝ REDUCE ANIMATION QUALITY WHEN CPU PRESSURE RISES. COMPUTE PRESSURE: IDEAS
  31. LONG TASKS: OVERVIEW new PerformanceObserver((list) = > { for (const

    entry of list.getEntries()) { console.log("Blocked for:", entry.duration); entry.attribution?.forEach((source) = > { console.log("Source:", source.containerSrc); }); } }).observe({ type: "longtask", buffered: true });
  32. DETECT: ↝ HYDRATION BLOCKING THE MAIN THREAD. ↝ LARGE BUNDLE

    EXECUTION PAUSES. ↝ THIRD-PARTY IFRAME BLOCKING INPUT. ↝ EXPENSIVE LAYOUT RECALCULATIONS. LONG TASKS: IDEAS
  33. DETECT: ↝ INP REGRESSIONS. ↝ RENDERING PIPELINE BOTTLENECKS. ↝ ANIMATION

    FRAME DROPS. ↝ SLOW CSS/LAYOUT RECALCULATIONS. ↝ SLOW COMPOSITING PHASES. LONG ANIMATION FRAMES: IDEAS
  34. SELF PROFILING: OVERVIEW const profiler = new Profiler({ sampleInterval: 10,

    maxBufferSize: 10000 }); await doWork(); const profile = await profiler.stop(); console.log(profile.samples);
  35. ↝ PROFILE REAL-WORLD HYDRATION BOTTLENECKS. ↝ PROFILE THIRD-PARTY SCRIPT EXECUTION

    TIME. ↝ DETECT HOT LOOPS IN DATA PROCESSING PIPELINES. ↝ DETECT REGRESSIONS AFTER FEATURE ROLLOUT. SELF PROFILING: IDEAS
  36. MEMORY USAGE: OVERVIEW const result = await performance.measureUserAgentSpecificMemory(); console.log("Total memory:",

    result.bytes); result.breakdown.forEach((entry) = > { console.log(entry.types, entry.bytes); });
  37. MEMORY USAGE: LEAKS #1 const obj = { a: new

    Array(1000), b: new Array(2000) }; setInterval(() = > { console.log(obj.a); }, 1000);
  38. ↝ FORGETTING TO UNREGISTER AN EVENT LISTENER. ↝ ACCIDENTALLY CAPTURING

    OBJECTS FROM AN IFRAME. ↝ NOT CLOSING A WORKER. ↝ ACCUMULATING OBJECTS IN ARRAYS. ↝ AND MUCH MORE! MEMORY USAGE: LEAKS
  39. ↝ DETECT MEMORY LEAKS AFTER ROUTE TRANSITIONS. ↝ DETECT IFRAME

    MEMORY ISOLATION ISSUES. ↝ DETECT DOM NODE ACCUMULATION REGRESSIONS. ↝ MEASURE SPA CACHE GROWTH OVER TIME. ↝ COMPARE THE MEMORY COST OF FEATURE VARIANTS. MEMORY USAGE: IDEAS
  40. PROFILING { name: "same-origin-descendant", entryType: "longtask", startTime: 1023.40999995591, duration: 187.19000002602115,

    attribution: [ { name: "unknown", entryType: "taskattribution", startTime: 0, duration: 0, containerType: "iframe", containerSrc: "child.html", containerId: "", containerName: "child1" } ] } { bytes: 1000000, breakdown: [ { bytes: 1000000, attribution: [ { url: "https://example.com", scope: "Window", }, ], types: ["JS", "DOM"], }, { bytes: 0, attribution: [], types: [], }, ], } { "frames": [ { "name": "Profiler" }, { "column": 0, "line": 100, "name": "", "resourceId": 0 }, { "name": "set innerHTML" }, { "column": 10, "line": 10, "name": "A", "resourceId": 1 } { "column": 20, "line": 20, "name": "B", "resourceId": 1 } ], "resources": [ "https://example.com/page", "https://example.com/app.js", ], "samples": [ { "stackId": 0, "timestamp": 161.99500000476837 }, { "stackId": 2, "timestamp": 182.43499994277954 }, { "timestamp": 197.43499994277954 }, { "timestamp": 213.32999992370605 }, { "stackId": 3, "timestamp": 228.59999990463257 }, ], "stacks": [ { "frameId": 0 }, { "frameId": 2 }, { "frameId": 3 }, { "frameId": 4, "parentId": 2 } ] }
  41. ↝ MAIN-THREAD BLOCKING ↝ WHERE TIME GOES. ↝ FRAME PIPELINE

    BLOCKING ↝ WHERE FRAMES DROP. ↝ CPU HOTSPOT ATTRIBUTION ↝ WHAT CODE RUNS. ↝ MEMORY HOTSPOT ATTRIBUTION ↝ WHERE MEMORY GROWS. PROFILING
  42. IMPROVING WITH THE PLATFORM PREDICT WORK PRECONNECT • SPECULATION RULES

    SCHEDULE WORK SCHEDULE TASKS • YIELDING PRIORITIZE WORK PRIORITY HINTS • NATIVE LAZY LOADING CONTINUITY WORK PRESERVE NAVIGATION • SMOOTH NAVIGATION
  43. ↝ MANY PERFORMANCE OPTIMIZATIONS CAN BE MADE WHEN WE CAN

    PREDICT WHAT USERS MIGHT DO. ↝ THERE ARE A SIMPLE BUT EFFECTIVE WAYS FOR DEVELOPERS TO HELP THE BROWSER STAY ONE STEP AHEAD OF THE USER AND KEEP PAGES FAST. PREDICT WORK
  44. ↝ PREDICT TAB NAVIGATION IN DASHBOARDS. ↝ PREFETCH THE NEXT

    ROUTE AFTER LOGIN SUCCESS. ↝ PRECONNECT ANALYTICS ENDPOINTS AFTER CONSENT. ↝ PRERENDER CHECKOUT AFTER CART INTERACTION. ↝ PREFETCH THE NEXT ARTICLE WHEN THE READER SCROLLS 75%. PREDICT WORK: IDEAS
  45. ↝ IT ALLOWS THE BROWSER TO SET UP EARLY CONNECTIONS

    BEFORE THE REQUEST IS ACTUALLY SENT TO THE SERVER. THIS INCLUDES: ↝ TLS NEGOTIATIONS ↝ TCP HANDSHAKES ↝ ELIMINATES ROUND-TRIP LATENCY. PRECONNECT: OVERVIEW
  46. PRECONNECT: OVERVIEW 100MS 200MS 300MS 400MS 500MS 600MS 700MS HTML

    CSS FONT 1 FONT 2 FONTS START LOADING FONTS RENDERED
  47. PRECONNECT: OVERVIEW 100MS 200MS 300MS 400MS 500MS 600MS 700MS HTML

    CSS FONT 1 FONT 2 FONTS START LOADING FONTS RENDERED FONT 1 FONT 2
  48. PRECONNECT: ↝ GOOGLE FONTS BEFORE CSS LOADS. ↝ THE API

    ORIGIN AFTER LOGIN SUCCESS. ↝ THE CDN HOSTING HERO IMAGE. ↝ THE ANALYTICS ENDPOINT AFTER CONSENT. ↝ THE CHECKOUT DOMAIN FROM THE PRODUCT PAGE. PRECONNECT: IDEAS
  49. ↝ DESIGNED TO IMPROVE PERFORMANCE FOR FUTURE DOCUMENT NAVIGATIONS. ↝

    A MODERN AND MORE EXPRESSIVE REPLACEMENT FOR OLDER PRERENDER TECHNIQUES. ↝ PREFETCH/PRERENDER DOCUMENTS AHEAD OF CLICKS. ↝ WORKS BEST FOR MPAS OR ROUTE-LEVEL TRANSITIONS. SPECULATION RULES: OVERVIEW
  50. SPECULATION RULES: OVERVIEW <script type="speculationrules"> { "prefetch": [{ "source": "list",

    "urls": ["/checkout"] }] } </script> <script type="speculationrules"> { "prerender": [{ "where": { "href_matches": "/product/ * " } }] } </script>
  51. ↝ PREFETCH /DASHBOARD AFTER LOGIN SUCCESS. ↝ PREFETCH /CHECKOUT AFTER

    CART INTERACTION. ↝ PREFETCH TOP RESULT LINKS IN THE VIEWPORT. ↝ PRERENDER LIKELY NEXT SETTINGS TAB. SPECULATION RULES: IDEAS
  52. ↝ TELL THE BROWSER WHAT MATTERS FIRST. ↝ OVERRIDE DEFAULT

    FETCH HEURISTICS WHEN NEEDED. ↝ OPTIMIZE LCP RESOURCES EXPLICITLY. PRIORITY HINTS: OVERVIEW
  53. PRIORITY HINTS: OVERVIEW < ! - - Increase the priority

    of the LCP image - - > <img src="image.jpg" fetchpriority="high" /> < ! - - Lower the priority of above-the-fold images - - > <ul class="carousel"> <img src="img/carousel-1.jpg" fetchpriority="high" /> <img src="img/carousel-2.jpg" fetchpriority="low" /> <img src="img/carousel-3.jpg" fetchpriority="low" /> </ul> < ! - - Reprioritize scripts - - > <script src="async_but_important.js" async fetchpriority="high"></script> <script src="blocking_but_unimportant.js" fetchpriority="low"></script>
  54. PRIORITY HINTS: OVERVIEW < ! - - Increase the priority

    of the LCP image - - > <img src="image.jpg" fetchpriority="high" /> < ! - - Lower the priority of above-the-fold images - - > <ul class="carousel"> <img src="img/carousel-1.jpg" fetchpriority="high" /> <img src="img/carousel-2.jpg" fetchpriority="low" /> <img src="img/carousel-3.jpg" fetchpriority="low" /> </ul> < ! - - Reprioritize scripts - - > <script src="async_but_important.js" async fetchpriority="high"></script> <script src="blocking_but_unimportant.js" fetchpriority="low"></script>
  55. PRIORITY HINTS: OVERVIEW / / Important validation data const user

    = await fetch("/user"); / / Less important content data const relatedPosts = await fetch("/posts/suggested", { priority: "low" });
  56. PRIORITY HINTS: OVERVIEW / / Important validation data const user

    = await fetch("/user"); / / Less important content data const relatedPosts = await fetch("/posts/suggested", { priority: "low" });
  57. ↝ BOOST HERO IMAGE PRIORITY. ↝ PRIORITIZE ROUTE TRANSITION BUNDLES.

    ↝ DEPRIORITIZE RECOMMENDATION PANELS. ↝ DEPRIORITIZE BELOW-FOLD IMAGES. ↝ CONTROL HYDRATION FETCH ORDERING. PRIORITY HINTS: IDEAS
  58. ↝ ZERO JAVASCRIPT. ↝ DEFER IMAGE AND IFRAME DOWNLOADS AUTOMATICALLY.

    ↝ LET THE BROWSER DECIDE OPTIMAL FETCH TIMING. ↝ REMOVE OFF-SCREEN CONTENT FROM THE CRITICAL RENDERING PATH. NATIVE LAZY LOADING: OVERVIEW
  59. NATIVE LAZY LOADING: OVERVIEW <img src="/gallery-1.jpg" loading="lazy" width="800" height="600" />

    <iframe src="https://maps.example.com" loading="lazy" width="600" height="400"> </iframe>
  60. ↝ PRODUCT GALLERY IMAGES. ↝ EMBEDDED YOUTUBE PLAYERS. ↝ MAPS

    AND CHAT WIDGETS. ↝ ANALYTICS DASHBOARDS BELOW THE FOLD. ↝ ROUTE-LEVEL IFRAMES IN MICRO-FRONTENDS. NATIVE LAZY LOADING: IDEAS
  61. “Lazy-loading iframes can lead to 2-3% median data savings, 1-2%

    FCP reductions, and 2% FID improvements at the 95th percentile.” Chrome team’s research • 2019
  62. ↝ NO PRIORITY CONTROL. ↝ NO CANCELLATION SEMANTICS. ↝ NO

    COORDINATION WITH THE BROWSER SCHEDULER. ↝ NO INPUT RESPONSIVENESS GUARANTEES. ↝ NO WAY TO BREAK LONG TASKS SAFELY. SCHEDULE WORK: CURRENT ISSUES
  63. ↝ SCHEDULE TASKS WITH EXPLICIT PRIORITY. ↝ COORDINATE WITH THE

    BROWSER RENDERING PIPELINE. ↝ REPLACE setTimeout( . . . , 0) HACKS. ↝ SUPPORTS CANCELLATION VIA SIGNALS. ↝ WORKS IN WORKERS TOO. SCHEDULE TASKS: OVERVIEW
  64. ↝ SPLIT LONG TASKS SAFELY. ↝ RETURN CONTROL TO THE

    BROWSER MID-EXECUTION. ↝ CONTINUE LATER WITH THE SAME PRIORITY. ↝ AVOID BLOCKING INPUT AND RENDERING. ↝ REPLACE MANUAL CHUNKING LOOPS. YIELDING: OVERVIEW
  65. ↝ RESTORE PAGES INSTANTLY ON BACK/FORWARD NAVIGATION. ↝ KEEP DOM

    + JAVASCRIPT HEAP ALIVE IN MEMORY. ↝ ELIMINATE RELOAD ENTIRELY. ↝ TURN NAVIGATION INTO RESTORATION. PRESERVE NAVIGATION: OVERVIEW
  66. PRESERVE NAVIGATION: MONITOR window.addEventListener('pageshow', (event) = > { if (event.persisted)

    { console.log('This page was restored from the bfcache.'); } else { console.log('This page was loaded normally.'); } });
  67. PRESERVE NAVIGATION: MONITOR window.addEventListener('pagehide', (event) = > { if (event.persisted)

    { console.log('This page might be entering the bfcache.'); } else { console.log('This page will unload normally and be discarded.'); } });
  68. PRESERVE NAVIGATION: MONITOR new PerformanceObserver(list = > { for (const

    entry of list.getEntries()) { console.log(entry.notRestoredReasons); } }).observe({ type: "navigation", buffered: true });
  69. ↝ ANIMATE TRANSITIONS BETWEEN DOM STATES. ↝ WORKS ACROSS SPA

    AND MPA NAVIGATION. ↝ MAINTAINS USER CONTEXT DURING CHANGE. ↝ REDUCES PERCEIVED LATENCY. SMOOTH NAVIGATION: OVERVIEW
  70. ↝ ANIMATE LIST → DETAIL NAVIGATION. ↝ SMOOTH DASHBOARD TAB

    SWITCHING. ↝ PRESERVE ELEMENT CONTINUITY BETWEEN ROUTES. ↝ REDUCE PERCEIVED LAYOUT SHIFT. ↝ MAKE MPAS FEEL LIKE NATIVE APPS. SMOOTH NAVIGATION: IDEAS
  71. ↝ PREAMBLE ↝ MEASURING WITH THE PLATFORM ↝ OPTIMIZING WITH

    THE PLATFORM ↝ WHY IT MATTERS ↝ CLOSING THOUGHTS THIS TALK PRESENTS…
  72. Brainwave analysis from the experiment revealed that participants had to

    concentrate up to 50% more when using websites via the slower connection. — Study conducted by Foviance on behalf of CA • 2011 FRUSTRATION ↝ STRESS
  73. Delayed web pages caused a 38% rise in mobile users'

    heart rates — equivalent to the anxiety of watching a horror movie alone. — Ericsson ConsumerLab • 2015 FRUSTRATION ↝ ANXIETY
  74. #1 Core Web Vitals are a better starting point than

    a fi nish line. — IN THE BLINK OF AN EYE • TIM KADLEC , 2024 WEB PERFORMANCE APIS THAT YOU (PROBABLY) NEVER KNEW EXISTED
  75. #2 Existing RUM tools are great—until scale, cost, or product-speci

    fi c questions force you to measure things yourself. WEB PERFORMANCE APIS THAT YOU (PROBABLY) NEVER KNEW EXISTED
  76. TIME UNTIL THE… ↝ SCROLL EFFECTS WORK. ↝ APP RESPONDS

    TO CLICKS. ↝ MOST MEANINGFUL VIDEOS/ANIMATIONS RUN. ↝ PRIMARY FEATURE SHOWS UP/IS INTERACTIVE. ↝ COOKIE BANNER SHOWS UP. VENDORS CAN’T MEASURE YOUR “DOR”
  77. #3 Test on real phones and networks. WEB PERFORMANCE APIS

    THAT YOU (PROBABLY) NEVER KNEW EXISTED
  78. #4 Avoid the observer e ff ect. WEB PERFORMANCE APIS

    THAT YOU (PROBABLY) NEVER KNEW EXISTED
  79. #5 Always try to correlate behavior analysis with contextual metrics

    and business outcomes to tell the full story. WEB PERFORMANCE APIS THAT YOU (PROBABLY) NEVER KNEW EXISTED
  80. #6 There's probably a business case for making your app

    faster. But web performance is about more than “just” business. WEB PERFORMANCE APIS THAT YOU (PROBABLY) NEVER KNEW EXISTED