[{"data":1,"prerenderedAt":1506},["ShallowReactive",2],{"content:\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Fsending-loaf-data-in-rum-beacons":3,"surroundings:\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Fsending-loaf-data-in-rum-beacons":1498},{"id":4,"title":5,"body":6,"description":1475,"extension":1476,"meta":1477,"navigation":309,"path":1492,"seo":1493,"stem":1496,"__hash__":1497},"content\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Fsending-loaf-data-in-rum-beacons\u002Findex.md","Sending LoAF Data in RUM Beacons",{"type":7,"value":8,"toc":1461},"minimark",[9,14,29,41,158,163,209,213,219,225,239,257,261,266,273,453,456,460,809,812,882,886,1101,1104,1108,1125,1167,1284,1288,1299,1303,1306,1312,1322,1333,1336,1386,1390,1407,1419,1423,1446,1451,1454,1457],[10,11,13],"h1",{"id":12},"how-to-send-loaf-attribution-in-rum-beacons-without-blowing-the-payload-budget","How to Send LoAF Attribution in RUM Beacons Without Blowing the Payload Budget",[15,16,17,18,23,24,28],"p",{},"This guide takes the ",[19,20,22],"a",{"href":21},"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002F","Long Animation Frames API"," out of the lab and into the ",[19,25,27],{"href":26},"\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002F","RUM beacons and field data collection"," pipeline that decides whether INP passes at p75.",[15,30,31,32,36,37,40],{},"The failure scenario is familiar to anyone who has tried it naively: a team serialises every LoAF entry into the beacon, payloads swell to 40KB on heavy sessions, ",[33,34,35],"code",{},"sendBeacon"," starts returning ",[33,38,39],{},"false"," because the browser's queued-payload limit (roughly 64KB per page) is exhausted, and the analytics warehouse fills with high-cardinality URL strings no one can group. The data is valuable precisely because it names scripts — but only a few rows per session are worth sending.",[15,42,43],{},[44,45,51,52,51,59,51,63,51,66,51,75,51,81,51,92,51,99,51,108,51,113,51,117,51,121,51,125,51,129,51,135,51,139,51,143,51,147,51,151,51],"svg",{"viewBox":46,"width":47,"role":48,"ariaLabel":49,"style":50},"0 0 760 216","100%","img","Bar chart comparing beacon payload sizes for four LoAF collection strategies against a 4KB target.","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",{},"Beacon size by collection strategy (heavy session)",[64,65,49],"desc",{},[53,67],{"x":68,"y":68,"width":69,"height":70,"rx":71,"fill":72,"stroke":73,"style":74},"1","758","214","10","none","currentColor","stroke-opacity:0.18",[76,77,62],"text",{"x":78,"y":79,"fill":73,"style":80},"28.0","34.0","font-size:16px;font-weight:700",[82,83],"line",{"x1":84,"y1":85,"x2":84,"y2":86,"stroke":87,"strokeWidth":88,"strokeDashArray":89,"style":91},"248.9","72.0","194.0","#b8860b","1.5",[90,90],"4","stroke-opacity:0.9",[76,93,98],{"x":94,"y":95,"fill":73,"style":96,"textAnchor":97},"187.6","92.0","font-size:13px","end","Every frame",[53,100],{"x":101,"y":102,"width":103,"height":104,"rx":105,"fill":106,"stroke":87,"style":107},"199.6","78.0","468.4","19","3","#ffc300","fill-opacity:0.7;stroke-opacity:0.9",[76,109,112],{"x":110,"y":95,"fill":73,"style":111},"674.0","font-size:12px;font-weight:600","38KB",[76,114,116],{"x":94,"y":115,"fill":73,"style":96,"textAnchor":97},"123.0","Frames over 50ms",[53,118],{"x":101,"y":119,"width":120,"height":104,"rx":105,"fill":106,"stroke":87,"style":107},"109.0","172.6",[76,122,124],{"x":123,"y":115,"fill":73,"style":111},"378.2","14KB",[76,126,128],{"x":94,"y":127,"fill":73,"style":96,"textAnchor":97},"154.0","INP frame",[53,130],{"x":101,"y":131,"width":132,"height":104,"rx":105,"fill":133,"stroke":133,"style":134},"140.0","14.8","#0466c8","fill-opacity:0.55;stroke-opacity:0.9",[76,136,138],{"x":137,"y":127,"fill":73,"style":111},"220.4","1.2KB",[76,140,142],{"x":94,"y":141,"fill":73,"style":96,"textAnchor":97},"185.0","INP frame + 2 worst frames",[53,144],{"x":101,"y":145,"width":146,"height":104,"rx":105,"fill":133,"stroke":133,"style":134},"171.0","32.0",[76,148,150],{"x":149,"y":141,"fill":73,"style":111},"237.7","2.6KB",[76,152,157],{"x":84,"y":153,"fill":154,"style":155,"textAnchor":156},"68.0","#8a6608","font-size:12px;font-weight:700","middle","4KB target",[159,160,162],"h2",{"id":161},"rapid-diagnosis","Rapid Diagnosis",[164,165,166,180,190,200],"ul",{},[167,168,169,176,177,179],"li",{},[170,171,172,173,175],"strong",{},"Check ",[33,174,35],{},"'s return value."," Log the boolean in a test session; ",[33,178,39],{}," means the payload was rejected for size and the data silently vanished.",[167,181,182,185,186,189],{},[170,183,184],{},"Measure payload size by percentile."," Instrument ",[33,187,188],{},"JSON.stringify(payload).length"," and look at p95, not the average — long sessions on low-end devices are exactly where frames pile up.",[167,191,192,199],{},[170,193,194,195,198],{},"Count distinct ",[33,196,197],{},"sourceURL"," values in the warehouse."," Tens of thousands of distinct values usually means query strings, cache busters or blob URLs are leaking cardinality.",[167,201,202,51,205,208],{},[170,203,204],{},"Audit for personal data.",[33,206,207],{},"invoker"," strings can contain element IDs and URLs; query strings on script URLs can carry tokens.",[159,210,212],{"id":211},"root-cause-analysis","Root Cause Analysis",[15,214,215,218],{},[170,216,217],{},"1. Sending frames that cannot affect INP."," Most long frames on a typical page happen during load, before any interaction. Only the frame that contains the INP interaction — and arguably the frame before it — explains the metric.",[15,220,221,224],{},[170,222,223],{},"2. Sending every script row."," A frame can list a dozen scripts. The top three by duration almost always cover more than 80% of script time; the rest is noise at the price of bytes.",[15,226,227,230,231,234,235,238],{},[170,228,229],{},"3. Unnormalised URLs."," Scripts served with ",[33,232,233],{},"?v=123",", signed CDN parameters, or ",[33,236,237],{},"blob:"," URLs make each session look unique. Grouping then fails, and per-row storage cost multiplies.",[15,240,241,244,245,248,249,252,253,256],{},[170,242,243],{},"4. Sending at the wrong moment."," Beacons fired on ",[33,246,247],{},"unload"," are unreliable on mobile; INP can also keep changing until the page is hidden. The beacon should be sent on ",[33,250,251],{},"visibilitychange"," to ",[33,254,255],{},"hidden",", once.",[159,258,260],{"id":259},"step-by-step-resolution","Step-by-Step Resolution",[262,263,265],"h3",{"id":264},"_1-use-the-inp-attribution-frames-not-your-own-observer-buffer","1. Use the INP attribution frames, not your own observer buffer",[15,267,268,269,272],{},"The ",[33,270,271],{},"web-vitals"," attribution build already selects the frames that overlap the INP interaction. Start there.",[274,275,280],"pre",{"className":276,"code":277,"language":278,"meta":279,"style":279},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","import { onINP } from 'web-vitals\u002Fattribution';\n\nlet inpPayload = null;\nonINP(({ value, attribution }) => {\n  inpPayload = {\n    inp: Math.round(value),\n    target: attribution.interactionTarget,\n    phase: dominantPhase(attribution),\n    frames: (attribution.longAnimationFrameEntries || []).map(trimFrame),\n  };\n}, { reportAllChanges: true });\n\u002F\u002F trade-off: reportAllChanges keeps inpPayload current as worse interactions\n\u002F\u002F occur, at the cost of running the callback more often. That is cheap here\n\u002F\u002F because we only store, never send, inside the callback.\n","javascript","",[33,281,282,304,311,329,358,368,380,386,398,416,422,434,441,447],{"__ignoreMap":279},[283,284,286,290,294,297,301],"span",{"class":82,"line":285},1,[283,287,289],{"class":288},"sPARh","import",[283,291,293],{"class":292},"saISM"," { onINP } ",[283,295,296],{"class":288},"from",[283,298,300],{"class":299},"sZ8jY"," 'web-vitals\u002Fattribution'",[283,302,303],{"class":292},";\n",[283,305,307],{"class":82,"line":306},2,[283,308,310],{"emptyLinePlaceholder":309},true,"\n",[283,312,314,317,320,323,327],{"class":82,"line":313},3,[283,315,316],{"class":288},"let",[283,318,319],{"class":292}," inpPayload ",[283,321,322],{"class":288},"=",[283,324,326],{"class":325},"sPXB4"," null",[283,328,303],{"class":292},[283,330,332,336,339,343,346,349,352,355],{"class":82,"line":331},4,[283,333,335],{"class":334},"smZ65","onINP",[283,337,338],{"class":292},"(({ ",[283,340,342],{"class":341},"sQw3B","value",[283,344,345],{"class":292},", ",[283,347,348],{"class":341},"attribution",[283,350,351],{"class":292}," }) ",[283,353,354],{"class":288},"=>",[283,356,357],{"class":292}," {\n",[283,359,361,364,366],{"class":82,"line":360},5,[283,362,363],{"class":292},"  inpPayload ",[283,365,322],{"class":288},[283,367,357],{"class":292},[283,369,371,374,377],{"class":82,"line":370},6,[283,372,373],{"class":292},"    inp: Math.",[283,375,376],{"class":334},"round",[283,378,379],{"class":292},"(value),\n",[283,381,383],{"class":82,"line":382},7,[283,384,385],{"class":292},"    target: attribution.interactionTarget,\n",[283,387,389,392,395],{"class":82,"line":388},8,[283,390,391],{"class":292},"    phase: ",[283,393,394],{"class":334},"dominantPhase",[283,396,397],{"class":292},"(attribution),\n",[283,399,401,404,407,410,413],{"class":82,"line":400},9,[283,402,403],{"class":292},"    frames: (attribution.longAnimationFrameEntries ",[283,405,406],{"class":288},"||",[283,408,409],{"class":292}," []).",[283,411,412],{"class":334},"map",[283,414,415],{"class":292},"(trimFrame),\n",[283,417,419],{"class":82,"line":418},10,[283,420,421],{"class":292},"  };\n",[283,423,425,428,431],{"class":82,"line":424},11,[283,426,427],{"class":292},"}, { reportAllChanges: ",[283,429,430],{"class":325},"true",[283,432,433],{"class":292}," });\n",[283,435,437],{"class":82,"line":436},12,[283,438,440],{"class":439},"sjfSM","\u002F\u002F trade-off: reportAllChanges keeps inpPayload current as worse interactions\n",[283,442,444],{"class":82,"line":443},13,[283,445,446],{"class":439},"\u002F\u002F occur, at the cost of running the callback more often. That is cheap here\n",[283,448,450],{"class":82,"line":449},14,[283,451,452],{"class":439},"\u002F\u002F because we only store, never send, inside the callback.\n",[15,454,455],{},"Expected outcome: payloads describe one interaction in depth instead of every frame shallowly.",[262,457,459],{"id":458},"_2-trim-each-frame-to-its-top-scripts-and-a-few-numbers","2. Trim each frame to its top scripts and a few numbers",[274,461,463],{"className":276,"code":462,"language":278,"meta":279,"style":279},"function normalizeUrl(u) {\n  if (!u) return '(inline)';\n  if (u.startsWith('blob:')) return '(blob)';\n  try { const url = new URL(u); return url.origin + url.pathname; } catch { return '(invalid)'; }\n}\nfunction trimFrame(f) {\n  return {\n    d: Math.round(f.duration),\n    b: Math.round(f.blockingDuration),\n    r: Math.round(f.startTime + f.duration - f.renderStart),\n    s: [...f.scripts].sort((a, b) => b.duration - a.duration).slice(0, 3).map((s) => ({\n      u: normalizeUrl(s.sourceURL),\n      f: (s.sourceFunctionName || '').slice(0, 60),\n      t: s.invokerType,\n      ms: Math.round(s.duration),\n      l: Math.round(s.forcedStyleAndLayoutDuration),\n    })),\n  };\n}\n\u002F\u002F trade-off: dropping sourceCharPosition saves bytes but removes the ability\n\u002F\u002F to source-map minified anonymous functions. Keep it for first-party origins\n\u002F\u002F if your bundles are minified and function names are mangled.\n",[33,464,465,482,504,529,579,584,598,605,615,625,646,711,722,748,753,764,775,781,786,791,797,803],{"__ignoreMap":279},[283,466,467,470,473,476,479],{"class":82,"line":285},[283,468,469],{"class":288},"function",[283,471,472],{"class":334}," normalizeUrl",[283,474,475],{"class":292},"(",[283,477,478],{"class":341},"u",[283,480,481],{"class":292},") {\n",[283,483,484,487,490,493,496,499,502],{"class":82,"line":306},[283,485,486],{"class":288},"  if",[283,488,489],{"class":292}," (",[283,491,492],{"class":288},"!",[283,494,495],{"class":292},"u) ",[283,497,498],{"class":288},"return",[283,500,501],{"class":299}," '(inline)'",[283,503,303],{"class":292},[283,505,506,508,511,514,516,519,522,524,527],{"class":82,"line":313},[283,507,486],{"class":288},[283,509,510],{"class":292}," (u.",[283,512,513],{"class":334},"startsWith",[283,515,475],{"class":292},[283,517,518],{"class":299},"'blob:'",[283,520,521],{"class":292},")) ",[283,523,498],{"class":288},[283,525,526],{"class":299}," '(blob)'",[283,528,303],{"class":292},[283,530,531,534,537,540,543,546,549,552,555,557,560,563,566,569,571,573,576],{"class":82,"line":331},[283,532,533],{"class":288},"  try",[283,535,536],{"class":292}," { ",[283,538,539],{"class":288},"const",[283,541,542],{"class":325}," url",[283,544,545],{"class":288}," =",[283,547,548],{"class":288}," new",[283,550,551],{"class":334}," URL",[283,553,554],{"class":292},"(u); ",[283,556,498],{"class":288},[283,558,559],{"class":292}," url.origin ",[283,561,562],{"class":288},"+",[283,564,565],{"class":292}," url.pathname; } ",[283,567,568],{"class":288},"catch",[283,570,536],{"class":292},[283,572,498],{"class":288},[283,574,575],{"class":299}," '(invalid)'",[283,577,578],{"class":292},"; }\n",[283,580,581],{"class":82,"line":360},[283,582,583],{"class":292},"}\n",[283,585,586,588,591,593,596],{"class":82,"line":370},[283,587,469],{"class":288},[283,589,590],{"class":334}," trimFrame",[283,592,475],{"class":292},[283,594,595],{"class":341},"f",[283,597,481],{"class":292},[283,599,600,603],{"class":82,"line":382},[283,601,602],{"class":288},"  return",[283,604,357],{"class":292},[283,606,607,610,612],{"class":82,"line":388},[283,608,609],{"class":292},"    d: Math.",[283,611,376],{"class":334},[283,613,614],{"class":292},"(f.duration),\n",[283,616,617,620,622],{"class":82,"line":400},[283,618,619],{"class":292},"    b: Math.",[283,621,376],{"class":334},[283,623,624],{"class":292},"(f.blockingDuration),\n",[283,626,627,630,632,635,637,640,643],{"class":82,"line":418},[283,628,629],{"class":292},"    r: Math.",[283,631,376],{"class":334},[283,633,634],{"class":292},"(f.startTime ",[283,636,562],{"class":288},[283,638,639],{"class":292}," f.duration ",[283,641,642],{"class":288},"-",[283,644,645],{"class":292}," f.renderStart),\n",[283,647,648,651,654,657,660,663,665,667,670,673,675,678,680,683,686,688,690,692,694,697,699,701,704,706,708],{"class":82,"line":424},[283,649,650],{"class":292},"    s: [",[283,652,653],{"class":288},"...",[283,655,656],{"class":292},"f.scripts].",[283,658,659],{"class":334},"sort",[283,661,662],{"class":292},"((",[283,664,19],{"class":341},[283,666,345],{"class":292},[283,668,669],{"class":341},"b",[283,671,672],{"class":292},") ",[283,674,354],{"class":288},[283,676,677],{"class":292}," b.duration ",[283,679,642],{"class":288},[283,681,682],{"class":292}," a.duration).",[283,684,685],{"class":334},"slice",[283,687,475],{"class":292},[283,689,57],{"class":325},[283,691,345],{"class":292},[283,693,105],{"class":325},[283,695,696],{"class":292},").",[283,698,412],{"class":334},[283,700,662],{"class":292},[283,702,703],{"class":341},"s",[283,705,672],{"class":292},[283,707,354],{"class":288},[283,709,710],{"class":292}," ({\n",[283,712,713,716,719],{"class":82,"line":436},[283,714,715],{"class":292},"      u: ",[283,717,718],{"class":334},"normalizeUrl",[283,720,721],{"class":292},"(s.sourceURL),\n",[283,723,724,727,729,732,734,736,738,740,742,745],{"class":82,"line":443},[283,725,726],{"class":292},"      f: (s.sourceFunctionName ",[283,728,406],{"class":288},[283,730,731],{"class":299}," ''",[283,733,696],{"class":292},[283,735,685],{"class":334},[283,737,475],{"class":292},[283,739,57],{"class":325},[283,741,345],{"class":292},[283,743,744],{"class":325},"60",[283,746,747],{"class":292},"),\n",[283,749,750],{"class":82,"line":449},[283,751,752],{"class":292},"      t: s.invokerType,\n",[283,754,756,759,761],{"class":82,"line":755},15,[283,757,758],{"class":292},"      ms: Math.",[283,760,376],{"class":334},[283,762,763],{"class":292},"(s.duration),\n",[283,765,767,770,772],{"class":82,"line":766},16,[283,768,769],{"class":292},"      l: Math.",[283,771,376],{"class":334},[283,773,774],{"class":292},"(s.forcedStyleAndLayoutDuration),\n",[283,776,778],{"class":82,"line":777},17,[283,779,780],{"class":292},"    })),\n",[283,782,784],{"class":82,"line":783},18,[283,785,421],{"class":292},[283,787,789],{"class":82,"line":788},19,[283,790,583],{"class":292},[283,792,794],{"class":82,"line":793},20,[283,795,796],{"class":439},"\u002F\u002F trade-off: dropping sourceCharPosition saves bytes but removes the ability\n",[283,798,800],{"class":82,"line":799},21,[283,801,802],{"class":439},"\u002F\u002F to source-map minified anonymous functions. Keep it for first-party origins\n",[283,804,806],{"class":82,"line":805},22,[283,807,808],{"class":439},"\u002F\u002F if your bundles are minified and function names are mangled.\n",[15,810,811],{},"Expected outcome: around 300–500 bytes per frame, with the field names short enough to matter at scale.",[15,813,814],{},[44,815,51,818,51,821,51,824,51,826,51,829,51,831,51,838,51,844,51,850,51,854,51,858,51,861,51,864,51,868,51,871,51,875,51,879,51],{"viewBox":816,"width":47,"role":48,"ariaLabel":817,"style":50},"0 0 760 268","Layers of a LoAF entry from full entry down to the trimmed beacon fields that are kept.",[53,819],{"className":820,"x":57,"y":57,"width":47,"height":47,"fill":58},[56],[60,822,823],{},"What survives into the beacon",[64,825,817],{},[53,827],{"x":68,"y":68,"width":69,"height":828,"rx":71,"fill":72,"stroke":73,"style":74},"266",[76,830,823],{"x":78,"y":79,"fill":73,"style":80},[53,832],{"x":78,"y":833,"width":834,"height":835,"rx":836,"fill":73,"stroke":73,"style":837},"56.0","704.0","40.0","6","fill-opacity:0.06;stroke-opacity:0.4",[76,839,843],{"x":840,"y":841,"fill":73,"style":842},"42.0","80.5","font-size:13px;font-weight:700","Full LoAF entry",[76,845,849],{"x":846,"y":841,"fill":73,"style":847,"textAnchor":848},"203.5","font-size:12px","start","20+ fields per frame, every script with invoker strings",[53,851],{"x":78,"y":852,"width":834,"height":835,"rx":836,"fill":133,"stroke":133,"style":853},"106.0","fill-opacity:0.14;stroke-opacity:0.9",[76,855,857],{"x":840,"y":856,"fill":73,"style":842},"130.5","INP-overlapping frames",[76,859,860],{"x":846,"y":856,"fill":73,"style":847,"textAnchor":848},"usually one or two frames per session",[53,862],{"x":78,"y":863,"width":834,"height":835,"rx":836,"fill":133,"stroke":133,"style":853},"156.0",[76,865,867],{"x":840,"y":866,"fill":73,"style":842},"180.5","Top 3 scripts each",[76,869,870],{"x":846,"y":866,"fill":73,"style":847,"textAnchor":848},"normalised URL, function, invoker type, duration, forced layout",[53,872],{"x":78,"y":873,"width":834,"height":835,"rx":836,"fill":106,"stroke":87,"style":874},"206.0","fill-opacity:0.24;stroke-opacity:0.9",[76,876,878],{"x":840,"y":877,"fill":73,"style":842},"230.5","Beacon payload",[76,880,881],{"x":846,"y":877,"fill":73,"style":847,"textAnchor":848},"about 1-3KB, sent once on visibilitychange",[262,883,885],{"id":884},"_3-send-once-on-hide-with-a-size-guard","3. Send once, on hide, with a size guard",[274,887,889],{"className":276,"code":888,"language":278,"meta":279,"style":279},"addEventListener('visibilitychange', () => {\n  if (document.visibilityState !== 'hidden' || !inpPayload) return;\n  let body = JSON.stringify(inpPayload);\n  if (body.length > 8000) {\n    inpPayload.frames = inpPayload.frames.slice(0, 1);   \u002F\u002F degrade, do not drop\n    body = JSON.stringify(inpPayload);\n  }\n  if (!navigator.sendBeacon('\u002Frum\u002Finp', body)) {\n    fetch('\u002Frum\u002Finp', { method: 'POST', body, keepalive: true });\n  }\n  inpPayload = null;\n}, { capture: true });\n\u002F\u002F trade-off: sending once on the first hide loses interactions that happen\n\u002F\u002F after the user returns to the tab. For long-lived SPAs, send on every hide\n\u002F\u002F and de-duplicate server-side by a session id.\n",[33,890,891,908,934,956,974,1000,1015,1020,1041,1063,1067,1077,1086,1091,1096],{"__ignoreMap":279},[283,892,893,896,898,901,904,906],{"class":82,"line":285},[283,894,895],{"class":334},"addEventListener",[283,897,475],{"class":292},[283,899,900],{"class":299},"'visibilitychange'",[283,902,903],{"class":292},", () ",[283,905,354],{"class":288},[283,907,357],{"class":292},[283,909,910,912,915,918,921,924,927,930,932],{"class":82,"line":306},[283,911,486],{"class":288},[283,913,914],{"class":292}," (document.visibilityState ",[283,916,917],{"class":288},"!==",[283,919,920],{"class":299}," 'hidden'",[283,922,923],{"class":288}," ||",[283,925,926],{"class":288}," !",[283,928,929],{"class":292},"inpPayload) ",[283,931,498],{"class":288},[283,933,303],{"class":292},[283,935,936,939,942,944,947,950,953],{"class":82,"line":313},[283,937,938],{"class":288},"  let",[283,940,941],{"class":292}," body ",[283,943,322],{"class":288},[283,945,946],{"class":325}," JSON",[283,948,949],{"class":292},".",[283,951,952],{"class":334},"stringify",[283,954,955],{"class":292},"(inpPayload);\n",[283,957,958,960,963,966,969,972],{"class":82,"line":331},[283,959,486],{"class":288},[283,961,962],{"class":292}," (body.",[283,964,965],{"class":325},"length",[283,967,968],{"class":288}," >",[283,970,971],{"class":325}," 8000",[283,973,481],{"class":292},[283,975,976,979,981,984,986,988,990,992,994,997],{"class":82,"line":360},[283,977,978],{"class":292},"    inpPayload.frames ",[283,980,322],{"class":288},[283,982,983],{"class":292}," inpPayload.frames.",[283,985,685],{"class":334},[283,987,475],{"class":292},[283,989,57],{"class":325},[283,991,345],{"class":292},[283,993,68],{"class":325},[283,995,996],{"class":292},");   ",[283,998,999],{"class":439},"\u002F\u002F degrade, do not drop\n",[283,1001,1002,1005,1007,1009,1011,1013],{"class":82,"line":370},[283,1003,1004],{"class":292},"    body ",[283,1006,322],{"class":288},[283,1008,946],{"class":325},[283,1010,949],{"class":292},[283,1012,952],{"class":334},[283,1014,955],{"class":292},[283,1016,1017],{"class":82,"line":382},[283,1018,1019],{"class":292},"  }\n",[283,1021,1022,1024,1026,1028,1031,1033,1035,1038],{"class":82,"line":388},[283,1023,486],{"class":288},[283,1025,489],{"class":292},[283,1027,492],{"class":288},[283,1029,1030],{"class":292},"navigator.",[283,1032,35],{"class":334},[283,1034,475],{"class":292},[283,1036,1037],{"class":299},"'\u002Frum\u002Finp'",[283,1039,1040],{"class":292},", body)) {\n",[283,1042,1043,1046,1048,1050,1053,1056,1059,1061],{"class":82,"line":400},[283,1044,1045],{"class":334},"    fetch",[283,1047,475],{"class":292},[283,1049,1037],{"class":299},[283,1051,1052],{"class":292},", { method: ",[283,1054,1055],{"class":299},"'POST'",[283,1057,1058],{"class":292},", body, keepalive: ",[283,1060,430],{"class":325},[283,1062,433],{"class":292},[283,1064,1065],{"class":82,"line":418},[283,1066,1019],{"class":292},[283,1068,1069,1071,1073,1075],{"class":82,"line":424},[283,1070,363],{"class":292},[283,1072,322],{"class":288},[283,1074,326],{"class":325},[283,1076,303],{"class":292},[283,1078,1079,1082,1084],{"class":82,"line":436},[283,1080,1081],{"class":292},"}, { capture: ",[283,1083,430],{"class":325},[283,1085,433],{"class":292},[283,1087,1088],{"class":82,"line":443},[283,1089,1090],{"class":439},"\u002F\u002F trade-off: sending once on the first hide loses interactions that happen\n",[283,1092,1093],{"class":82,"line":449},[283,1094,1095],{"class":439},"\u002F\u002F after the user returns to the tab. For long-lived SPAs, send on every hide\n",[283,1097,1098],{"class":82,"line":755},[283,1099,1100],{"class":439},"\u002F\u002F and de-duplicate server-side by a session id.\n",[15,1102,1103],{},"Expected outcome: delivery rates for INP attribution climb back above 95% of sessions on mobile.",[262,1105,1107],{"id":1106},"_4-strip-personal-data-and-cap-cardinality-server-side","4. Strip personal data and cap cardinality server-side",[15,1109,1110,1111,1114,1115,1117,1118,1121,1122,949],{},"Even after client trimming, normalise again at ingest: allow-list origins you recognise, bucket unknown third-party origins under ",[33,1112,1113],{},"other",", and drop ",[33,1116,207],{}," strings that contain digits longer than eight characters (likely IDs). Then store scripts as a child table keyed by session so you can aggregate ",[33,1119,1120],{},"sum(ms)"," by ",[33,1123,1124],{},"u, f",[274,1126,1130],{"className":1127,"code":1128,"language":1129,"meta":279,"style":279},"language-sql shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","-- Top scripts in INP frames over the last 7 days, p75-failing sessions only.\nSELECT s.u, s.f, s.t, count(*) AS sessions, approx_quantiles(s.ms, 100)[OFFSET(75)] AS p75_ms\nFROM rum_inp i, UNNEST(i.frames) f, UNNEST(f.s) s\nWHERE i.inp > 200 AND i.ts > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)\nGROUP BY 1, 2, 3 ORDER BY sessions DESC LIMIT 20;\n-- trade-off: filtering to inp > 200 shows what breaks failing sessions but\n-- hides scripts that are slow everywhere. Run the query unfiltered too.\n","sql",[33,1131,1132,1137,1142,1147,1152,1157,1162],{"__ignoreMap":279},[283,1133,1134],{"class":82,"line":285},[283,1135,1136],{},"-- Top scripts in INP frames over the last 7 days, p75-failing sessions only.\n",[283,1138,1139],{"class":82,"line":306},[283,1140,1141],{},"SELECT s.u, s.f, s.t, count(*) AS sessions, approx_quantiles(s.ms, 100)[OFFSET(75)] AS p75_ms\n",[283,1143,1144],{"class":82,"line":313},[283,1145,1146],{},"FROM rum_inp i, UNNEST(i.frames) f, UNNEST(f.s) s\n",[283,1148,1149],{"class":82,"line":331},[283,1150,1151],{},"WHERE i.inp > 200 AND i.ts > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)\n",[283,1153,1154],{"class":82,"line":360},[283,1155,1156],{},"GROUP BY 1, 2, 3 ORDER BY sessions DESC LIMIT 20;\n",[283,1158,1159],{"class":82,"line":370},[283,1160,1161],{},"-- trade-off: filtering to inp > 200 shows what breaks failing sessions but\n",[283,1163,1164],{"class":82,"line":382},[283,1165,1166],{},"-- hides scripts that are slow everywhere. Run the query unfiltered too.\n",[15,1168,1169],{},[44,1170,51,1173,51,1176,51,1179,51,1181,51,1198,51,1201,51,1203,51,1207,51,1211,51,1214,51,1218,51,1221,51,1225,51,1228,51,1232,51,1238,51,1241,51,1244,51,1247,51,1253,51,1258,51,1262,51,1267,51,1270,51,1275,51,1280,51],{"viewBox":1171,"width":47,"role":48,"ariaLabel":1172,"style":50},"0 0 760 290","Sequence from the browser recording an interaction to the warehouse producing a ranked script report.",[53,1174],{"className":1175,"x":57,"y":57,"width":47,"height":47,"fill":58},[56],[60,1177,1178],{},"Field attribution round trip",[64,1180,1172],{},[1182,1183,1184],"defs",{},[1185,1186,1193],"marker",{"id":1187,"viewBox":1188,"refX":1189,"refY":1190,"markerWidth":1191,"markerHeight":1191,"orient":1192},"fafd58f360","0 0 10 10","9","5","7","auto-start-reverse",[1194,1195],"path",{"d":1196,"fill":73,"style":1197},"M0 0 L10 5 L0 10 z","fill-opacity:0.7",[53,1199],{"x":68,"y":68,"width":69,"height":1200,"rx":71,"fill":72,"stroke":73,"style":74},"288",[76,1202,1178],{"x":78,"y":79,"fill":73,"style":80},[53,1204],{"x":1205,"y":833,"width":1206,"height":79,"rx":836,"fill":73,"stroke":73,"style":837},"41.0","150.0",[76,1208,1210],{"x":1209,"y":102,"fill":73,"style":842,"textAnchor":156},"116.0","Browser",[53,1212],{"x":1213,"y":833,"width":1206,"height":79,"rx":836,"fill":73,"stroke":73,"style":837},"217.0",[76,1215,1217],{"x":1216,"y":102,"fill":73,"style":842,"textAnchor":156},"292.0","Collector",[53,1219],{"x":1220,"y":833,"width":1206,"height":79,"rx":836,"fill":73,"stroke":73,"style":837},"393.0",[76,1222,1224],{"x":1223,"y":102,"fill":73,"style":842,"textAnchor":156},"468.0","Warehouse",[53,1226],{"x":1227,"y":833,"width":1206,"height":79,"rx":836,"fill":73,"stroke":73,"style":837},"569.0",[76,1229,1231],{"x":1230,"y":102,"fill":73,"style":842,"textAnchor":156},"644.0","Engineer",[82,1233],{"x1":1209,"y1":1234,"x2":1209,"y2":1235,"stroke":73,"strokeWidth":88,"strokeDashArray":1236,"style":1237},"90.0","268.0",[90,90],"stroke-opacity:0.35",[82,1239],{"x1":1216,"y1":1234,"x2":1216,"y2":1235,"stroke":73,"strokeWidth":88,"strokeDashArray":1240,"style":1237},[90,90],[82,1242],{"x1":1223,"y1":1234,"x2":1223,"y2":1235,"stroke":73,"strokeWidth":88,"strokeDashArray":1243,"style":1237},[90,90],[82,1245],{"x1":1230,"y1":1234,"x2":1230,"y2":1235,"stroke":73,"strokeWidth":88,"strokeDashArray":1246,"style":1237},[90,90],[82,1248],{"x1":1209,"y1":1249,"x2":1250,"y2":1249,"stroke":73,"strokeWidth":88,"style":1251,"markerEnd":1252},"124.0","290.0","stroke-opacity:0.6","url(#fafd58f360)",[76,1254,1257],{"x":1255,"y":1256,"fill":73,"style":847,"textAnchor":156},"204.0","117.0","beacon on hide (2KB)",[82,1259],{"x1":1216,"y1":1260,"x2":1261,"y2":1260,"stroke":73,"strokeWidth":88,"style":1251,"markerEnd":1252},"164.0","466.0",[76,1263,1266],{"x":1264,"y":1265,"fill":73,"style":847,"textAnchor":156},"380.0","157.0","normalised rows",[82,1268],{"x1":1230,"y1":1255,"x2":1269,"y2":1255,"stroke":73,"strokeWidth":88,"style":1251,"markerEnd":1252},"470.0",[76,1271,1274],{"x":1272,"y":1273,"fill":73,"style":847,"textAnchor":156},"556.0","197.0","top-scripts query",[82,1276],{"x1":1223,"y1":1277,"x2":1278,"y2":1277,"stroke":73,"strokeWidth":88,"strokeDashArray":1279,"style":1251,"markerEnd":1252},"244.0","642.0",[1190,90],[76,1281,1283],{"x":1272,"y":1282,"fill":73,"style":847,"textAnchor":156},"237.0","ranked owners",[159,1285,1287],{"id":1286},"verification","Verification",[15,1289,1290,1291,1293,1294,1298],{},"Compare a week before and after: the median beacon should be a few kilobytes at most, ",[33,1292,35],{}," failures should be near zero, and the \"top scripts in INP frames\" query should return a short list where the first five rows account for most failing sessions. Cross-check one of those rows in the lab with the method in ",[19,1295,1297],{"href":1296},"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Ffinding-slow-scripts-with-loaf-script-attribution\u002F","finding slow scripts with LoAF script attribution","; the field and lab should agree on the culprit even if the milliseconds differ.",[159,1300,1302],{"id":1301},"designing-the-schema-for-questions-you-will-actually-ask","Designing the Schema for Questions You Will Actually Ask",[15,1304,1305],{},"Beacon data is only useful if the warehouse can answer the questions engineers bring to it. Three questions come up repeatedly, and the schema should make each a single query.",[15,1307,1308,1311],{},[170,1309,1310],{},"\"Which script is responsible for most failing interactions this week?\""," Needs the script rows joined to the session's INP value and a time column. Store scripts as a repeated child record of the INP event rather than as a JSON string; string blobs force a parse in every query and defeat columnar compression.",[15,1313,1314,1317,1318,1321],{},[170,1315,1316],{},"\"Did release 4.12 make things worse?\""," Needs a build or release identifier on every beacon. Without it, a regression introduced on Tuesday and a traffic-mix change on Wednesday look identical. Add the identifier from a ",[33,1319,1320],{},"\u003Cmeta>"," tag rendered at build time, not from a runtime fetch.",[15,1323,1324,1327,1328,1332],{},[170,1325,1326],{},"\"Is this a device problem or a code problem?\""," Needs device memory, hardware concurrency and effective connection type captured with the beacon — the dimensions covered in ",[19,1329,1331],{"href":1330},"\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002Fsegmenting-rum-by-device-and-connection\u002F","segmenting RUM by device and connection",". A script that costs 40ms on 8-core devices and 220ms on 4-core devices is a different conversation from one that is slow everywhere.",[15,1334,1335],{},"Keep the field names short in the beacon and expand them at ingest. Bytes on the wire matter on a mobile network; column names in the warehouse should be self-explanatory for the person running the query six months from now.",[15,1337,1338],{},[44,1339,51,1341,51,1344,51,1347,51,1349,51,1351,51,1353,51,1355,51,1358,51,1362,51,1364,51,1367,51,1370,51,1372,51,1375,51,1378,51,1380,51,1383,51],{"viewBox":816,"width":47,"role":48,"ariaLabel":1340,"style":50},"Layers of a stored INP event, from the session record through the interaction to frames and script rows.",[53,1342],{"className":1343,"x":57,"y":57,"width":47,"height":47,"fill":58},[56],[60,1345,1346],{},"INP event schema that answers the common questions",[64,1348,1340],{},[53,1350],{"x":68,"y":68,"width":69,"height":828,"rx":71,"fill":72,"stroke":73,"style":74},[76,1352,1346],{"x":78,"y":79,"fill":73,"style":80},[53,1354],{"x":78,"y":833,"width":834,"height":835,"rx":836,"fill":73,"stroke":73,"style":837},[76,1356,1357],{"x":840,"y":841,"fill":73,"style":842},"Session",[76,1359,1361],{"x":1360,"y":841,"fill":73,"style":847,"textAnchor":848},"152.3","release id, device memory, cores, connection type, page template",[53,1363],{"x":78,"y":852,"width":834,"height":835,"rx":836,"fill":133,"stroke":133,"style":853},[76,1365,1366],{"x":840,"y":856,"fill":73,"style":842},"INP interaction",[76,1368,1369],{"x":1360,"y":856,"fill":73,"style":847,"textAnchor":848},"value, target selector, dominant phase, timestamp",[53,1371],{"x":78,"y":863,"width":834,"height":835,"rx":836,"fill":133,"stroke":133,"style":853},[76,1373,1374],{"x":840,"y":866,"fill":73,"style":842},"Frames",[76,1376,1377],{"x":1360,"y":866,"fill":73,"style":847,"textAnchor":848},"duration, blocking duration, render tail",[53,1379],{"x":78,"y":873,"width":834,"height":835,"rx":836,"fill":106,"stroke":87,"style":874},[76,1381,1382],{"x":840,"y":877,"fill":73,"style":842},"Script rows",[76,1384,1385],{"x":1360,"y":877,"fill":73,"style":847,"textAnchor":848},"normalised URL, function, invoker type, ms, forced layout ms",[159,1387,1389],{"id":1388},"faq","FAQ",[1391,1392,1395,1399],"details",{"className":1393},[1394],"faq-item",[1396,1397,1398],"summary",{},"Should I sample LoAF collection separately from the rest of RUM?",[15,1400,1401,1402,1406],{},"Usually not. INP attribution is most valuable on exactly the sessions that fail, and independent sampling discards some of them at random. If volume is a concern, sample whole sessions consistently — the approach in ",[19,1403,1405],{"href":1404},"\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002Fsampling-rum-data-without-biasing-p75\u002F","sampling RUM data without biasing p75"," — and keep attribution on every sampled session.",[1391,1408,1410,1413],{"className":1409},[1394],[1396,1411,1412],{},"Is it safe to send script URLs to an analytics vendor?",[15,1414,1415,1416,1418],{},"Script URLs themselves are rarely sensitive, but query strings and inline ",[33,1417,207],{}," values can be. Normalising to origin plus path and allow-listing origins removes almost all risk. Treat the field as you would a page URL in your privacy review.",[159,1420,1422],{"id":1421},"related","Related",[164,1424,1425,1432,1439],{},[167,1426,1427,1431],{},[19,1428,1430],{"href":1429},"\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002Fcomputing-p75-from-raw-rum-beacons\u002F","Computing p75 from raw RUM beacons"," — turning stored beacons into the number that passes or fails.",[167,1433,1434,1438],{},[19,1435,1437],{"href":1436},"\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002Fsending-web-vitals-with-sendbeacon-on-visibility-change\u002F","Sending web vitals with sendBeacon on visibility change"," — the delivery mechanics in depth.",[167,1440,1441,1445],{},[19,1442,1444],{"href":1443},"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Floaf-vs-long-tasks-api\u002F","LoAF vs the Long Tasks API"," — which entries are worth collecting at all.",[1447,1448,1450],"script",{"type":1449},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Send LoAF Attribution in RUM Beacons Without Blowing the Payload Budget\",\n  \"description\": \"Collect field attribution for slow interactions from Long Animation Frames while keeping beacons small, private and queryable.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Use the INP attribution frames, not your own observer buffer\",\n      \"text\": \"The web-vitals attribution build already selects the frames that overlap the INP interaction.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Trim each frame to its top scripts and a few numbers\",\n      \"text\": \"Expected outcome: around 300–500 bytes per frame, with the field names short enough to matter at scale.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Send once, on hide, with a size guard\",\n      \"text\": \"Expected outcome: delivery rates for INP attribution climb back above 95% of sessions on mobile.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Strip personal data and cap cardinality server-side\",\n      \"text\": \"Even after client trimming, normalise again at ingest: allow-list origins you recognise, bucket unknown third-party origins under other, and drop invoker strings that contain digits longer than eight characters (likely IDs).\"\n    }\n  ]\n}\n",[1447,1452,1453],{"type":1449},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Send LoAF Attribution in RUM Beacons Without Blowing the Payload Budget\",\n  \"description\": \"Collect field attribution for slow interactions from Long Animation Frames while keeping beacons small, private and queryable.\",\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\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Fsending-loaf-data-in-rum-beacons\u002F\"\n  }\n}\n",[1447,1455,1456],{"type":1449},"\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\": \"Core Web Vitals & Measurement\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Long Animation Frames (LoAF) API\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Sending LoAF Data in RUM Beacons\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Fsending-loaf-data-in-rum-beacons\u002F\"\n    }\n  ]\n}\n",[1458,1459,1460],"style",{},"html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}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 .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}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":279,"searchDepth":306,"depth":306,"links":1462},[1463,1464,1465,1471,1472,1473,1474],{"id":161,"depth":306,"text":162},{"id":211,"depth":306,"text":212},{"id":259,"depth":306,"text":260,"children":1466},[1467,1468,1469,1470],{"id":264,"depth":313,"text":265},{"id":458,"depth":313,"text":459},{"id":884,"depth":313,"text":885},{"id":1106,"depth":313,"text":1107},{"id":1286,"depth":306,"text":1287},{"id":1301,"depth":306,"text":1302},{"id":1388,"depth":306,"text":1389},{"id":1421,"depth":306,"text":1422},"Collect field attribution for slow interactions from Long Animation Frames while keeping beacons small, private and queryable.","md",{"slug":1478,"type":1479,"breadcrumb":1480,"datePublished":1491,"dateModified":1491},"sending-loaf-data-in-rum-beacons","article",[1481,1484,1487,1489],{"name":1482,"url":1483},"Home","\u002F",{"name":1485,"url":1486},"Core Web Vitals & Measurement","\u002Fcore-web-vitals-measurement\u002F",{"name":1488,"url":21},"Long Animation Frames (LoAF) API",{"name":5,"url":1490},"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Fsending-loaf-data-in-rum-beacons\u002F","2026-10-06","\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Fsending-loaf-data-in-rum-beacons",{"title":1494,"description":1495},"Sending LoAF Data in RUM Beacons Without Bloat","Ship Long Animation Frame attribution from real users within a small beacon budget: pick the frames that matter, trim script rows and keep cardinality low.","core-web-vitals-measurement\u002Flong-animation-frames-api\u002Fsending-loaf-data-in-rum-beacons\u002Findex","zQ3IzWmlh9iHLZPBnTvnGosI2ymZv70JodqImS1UmbY",[1499,1502],{"title":1444,"path":1500,"stem":1501,"children":-1},"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Floaf-vs-long-tasks-api","core-web-vitals-measurement\u002Flong-animation-frames-api\u002Floaf-vs-long-tasks-api\u002Findex",{"title":1503,"path":1504,"stem":1505,"children":-1},"Measuring LCP with Chrome DevTools","\u002Fcore-web-vitals-measurement\u002Fmeasuring-lcp-with-chrome-devtools","core-web-vitals-measurement\u002Fmeasuring-lcp-with-chrome-devtools\u002Findex",1791308074147]