[{"data":1,"prerenderedAt":1347},["ShallowReactive",2],{"content:\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection":3,"surroundings:\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection":1338},{"id":4,"title":5,"body":6,"description":1320,"extension":1321,"meta":1322,"navigation":351,"path":1332,"seo":1333,"stem":1336,"__hash__":1337},"content\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002Findex.md","RUM Beacons & Field Data Collection",{"type":7,"value":8,"toc":1308},"minimark",[9,14,41,57,225,230,233,273,276,280,308,312,315,620,630,634,637,664,683,689,909,920,924,927,1030,1037,1040,1044,1055,1081,1088,1092,1102,1112,1118,1124,1128,1131,1181,1192,1200,1204,1216,1225,1234,1246,1250,1290,1295,1298,1301,1304],[10,11,13],"h1",{"id":12},"rum-beacons-field-data-collection-turning-p75-into-an-actionable-signal","RUM Beacons & Field Data Collection: Turning p75 Into an Actionable Signal",[15,16,17,18,23,24,28,29,32,33,36,37,40],"p",{},"This guide operationalises the measurement half of ",[19,20,22],"a",{"href":21},"\u002Fcore-web-vitals-measurement\u002F","Core Web Vitals & Measurement",": where the lab tells you ",[25,26,27],"em",{},"why"," a page is slow, a real-user monitoring beacon tells you ",[25,30,31],{},"for whom",", ",[25,34,35],{},"how often",", and ",[25,38,39],{},"by how much",".",[15,42,43,44,48,49,52,53,56],{},"Every Core Web Vitals threshold — ",[45,46,47],"code",{},"2.5s"," for LCP, ",[45,50,51],{},"200ms"," for INP, ",[45,54,55],{},"0.1"," for CLS — is assessed at the 75th percentile of real visits. That single fact makes field collection non-optional: a synthetic run on a fixed device and a fixed network produces one sample from a distribution whose shape you cannot see. Teams that ship only against Lighthouse routinely discover that their p50 is excellent, their p75 fails, and the gap is entirely explained by a device class or a route they never tested. A RUM pipeline closes that gap, but only if the beacon carries enough attribution to name the element or the handler responsible — a bare number tells you that you failed without telling you what to change.",[58,59,66,67,74,75,74,79,74,83,74,92,74,99,74,108,74,115,74,120,74,127,74,132,74,136,74,140,74,143,74,147,74,150,74,156,74,160,74,163,74,167,74,170,74,174,74,178,74,181,74,185,74,188,74,194,74,199,74,203,74,207,74,210,74,213,74,217,74,220,74],"svg",{"xmlns":60,"viewBox":61,"width":62,"role":63,"ariaLabel":64,"style":65},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 300","100%","img","Pipeline diagram from the browser through the web-vitals library, a queued beacon, an edge collector and storage, to a percentile dashboard and CI alerting","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)","\n  ",[68,69],"rect",{"className":70,"x":72,"y":72,"width":62,"height":62,"fill":73},[71],"svg-canvas","0","#ffffff"," ",[76,77,78],"title",{},"The field-data pipeline, end to end",[80,81,82],"desc",{},"Browser performance entries flow through the web-vitals library into a batched beacon queue, are flushed on page hide to an edge collector, stored, then aggregated into p75 dashboards and regression alerts.",[68,84],{"x":85,"y":85,"width":86,"height":87,"rx":88,"fill":89,"stroke":90,"style":91},"1","758","298","10","none","currentColor","stroke-opacity:0.18",[93,94,98],"text",{"x":95,"y":96,"fill":90,"style":97},"24","36","font-size:17px;font-weight:700","From PerformanceObserver to a p75 you can ship against",[68,100],{"x":101,"y":102,"width":103,"height":104,"rx":105,"fill":106,"stroke":106,"style":107},"28","70","150","62","7","#0466c8","fill-opacity:0.14;stroke-opacity:0.5",[93,109,114],{"x":110,"y":111,"fill":90,"style":112,"textAnchor":113},"103","95","font-size:12.5px;font-weight:700;","middle","Browser",[93,116,119],{"x":110,"y":117,"fill":90,"style":118,"textAnchor":113},"114","font-size:11px;","LCP \u002F INP \u002F CLS entries",[121,122],"line",{"x1":123,"y1":124,"x2":125,"y2":124,"stroke":90,"style":126},"178","101","204","stroke-opacity:0.4",[128,129],"polygon",{"points":130,"fill":90,"style":131},"212,101 202,96 202,106","fill-opacity:0.55",[68,133],{"x":134,"y":102,"width":103,"height":104,"rx":105,"fill":90,"stroke":90,"style":135},"212","fill-opacity:0.05;stroke-opacity:0.32",[93,137,139],{"x":138,"y":111,"fill":90,"style":112,"textAnchor":113},"287","web-vitals",[93,141,142],{"x":138,"y":117,"fill":90,"style":118,"textAnchor":113},"attribution build",[121,144],{"x1":145,"y1":124,"x2":146,"y2":124,"stroke":90,"style":126},"362","388",[128,148],{"points":149,"fill":90,"style":131},"396,101 386,96 386,106",[68,151],{"x":152,"y":102,"width":103,"height":104,"rx":105,"fill":153,"stroke":154,"style":155},"396","#ffc300","#b8860b","fill-opacity:0.22",[93,157,159],{"x":158,"y":111,"fill":90,"style":112,"textAnchor":113},"471","Beacon queue",[93,161,162],{"x":158,"y":117,"fill":90,"style":118,"textAnchor":113},"flush on page hide",[121,164],{"x1":165,"y1":124,"x2":166,"y2":124,"stroke":90,"style":126},"546","572",[128,168],{"points":169,"fill":90,"style":131},"580,101 570,96 570,106",[68,171],{"x":172,"y":102,"width":173,"height":104,"rx":105,"fill":90,"stroke":90,"style":135},"580","152",[93,175,177],{"x":176,"y":111,"fill":90,"style":112,"textAnchor":113},"656","Edge collector",[93,179,180],{"x":176,"y":117,"fill":90,"style":118,"textAnchor":113},"204, no body",[121,182],{"x1":176,"y1":183,"x2":176,"y2":184,"stroke":90,"style":126},"132","166",[128,186],{"points":187,"fill":90,"style":131},"656,174 651,164 661,164",[68,189],{"x":190,"y":191,"width":192,"height":193,"rx":105,"fill":90,"stroke":90,"style":135},"470","174","262","52",[93,195,198],{"x":196,"y":197,"fill":90,"style":112,"textAnchor":113},"601","196","Columnar store",[93,200,202],{"x":196,"y":201,"fill":90,"style":118,"textAnchor":113},"214","one row per metric per visit",[121,204],{"x1":190,"y1":205,"x2":206,"y2":205,"stroke":90,"style":126},"200","440",[128,208],{"points":209,"fill":90,"style":131},"432,200 442,195 442,205",[68,211],{"x":212,"y":191,"width":192,"height":193,"rx":105,"fill":106,"stroke":106,"style":107},"170",[93,214,216],{"x":215,"y":197,"fill":90,"style":112,"textAnchor":113},"301","p75 by route × device × country",[93,218,219],{"x":215,"y":201,"fill":90,"style":118,"textAnchor":113},"dashboards + regression alerts",[93,221,224],{"x":95,"y":222,"fill":90,"style":223},"264","font-size:11.5px;font-style:italic;fill-opacity:0.8","Attribution travels with the number: without the element or the handler name, a failing p75 is a fact you cannot act on.",[226,227,229],"h2",{"id":228},"what-a-beacon-must-carry-to-be-useful","What a Beacon Must Carry to Be Useful",[15,231,232],{},"A metric value alone answers \"are we passing?\". Engineering needs the answer to \"what do I change on Monday?\", and that requires four classes of payload on every beacon.",[234,235,236,255,261,267],"ul",{},[237,238,239,243,244,247,248,247,251,254],"li",{},[240,241,242],"strong",{},"The metric and its rating."," Name, value, and the ",[45,245,246],{},"good","\u002F",[45,249,250],{},"needs-improvement",[45,252,253],{},"poor"," bucket the library assigned, so dashboards do not have to re-derive thresholds that may change.",[237,256,257,260],{},[240,258,259],{},"Attribution."," For LCP, the element selector and the four timing phases. For INP, the interaction target, the event type, and the input\u002Fprocessing\u002Fpresentation split. For CLS, the largest shift source and the elements that moved. This is what turns a number into a ticket.",[237,262,263,266],{},[240,264,265],{},"Dimensions you will actually segment by."," Route pattern (not the raw URL — cardinality explodes), device memory and hardware concurrency bucket, effective connection type, country, and whether the visit was a cold or warm cache load.",[237,268,269,272],{},[240,270,271],{},"Delivery context."," A visit ID so multiple metrics from one page view can be joined, and a navigation type so back\u002Fforward-cache restores are analysed separately rather than skewing your cold-load p75.",[15,274,275],{},"The temptation to log everything is strong and expensive. Raw URLs, full user agents and unbounded custom properties inflate storage cost and make the aggregation query slow enough that nobody opens the dashboard. Fix the schema early: a dozen well-chosen dimensions outperform fifty ad-hoc ones.",[226,277,279],{"id":278},"prerequisites","Prerequisites",[234,281,282,295,302,305],{},[237,283,284,285,287,288,290,291,294],{},"The ",[45,286,139],{}," library v4 or later, using the ",[240,289,142],{}," — the standard build reports values only, and re-deriving attribution by hand from raw ",[45,292,293],{},"PerformanceObserver"," entries is error-prone for INP in particular.",[237,296,297,298,301],{},"An endpoint that answers ",[45,299,300],{},"204 No Content"," quickly and does no synchronous work. A Worker at the edge is ideal; a route on your application origin will do, provided it never touches the database on the request path.",[237,303,304],{},"A storage layer that can compute percentiles over tens of millions of rows — a columnar warehouse, a time-series database, or a hosted analytics product. Percentiles over a relational row store will be the thing that eventually kills the project.",[237,306,307],{},"Agreement on a sampling rate before launch. Retrofitting sampling after the fact invalidates historical comparisons.",[226,309,311],{"id":310},"_1-instrument-the-metrics-with-attribution","1. Instrument the Metrics with Attribution",[15,313,314],{},"Load the attribution build and register every metric once, at the top of the app's entry point. Registration is cheap; the observers are passive and the callbacks fire at most once per metric per page view.",[316,317,322],"pre",{"className":318,"code":319,"language":320,"meta":321,"style":321},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","import { onLCP, onINP, onCLS, onTTFB } from 'web-vitals\u002Fattribution';\n\nconst queue = new Set();\n\nfunction record(metric) {\n  const a = metric.attribution || {};\n  queue.add({\n    name: metric.name,                       \u002F\u002F LCP | INP | CLS | TTFB\n    value: Math.round(metric.value * 1000) \u002F 1000,\n    rating: metric.rating,\n    navType: metric.navigationType,          \u002F\u002F navigate | back-forward-cache | reload\n    target: a.interactionTarget || a.element || a.largestShiftTarget || null,\n    phases: {\n      ttfb: a.timeToFirstByte, delay: a.inputDelay ?? a.resourceLoadDelay,\n      process: a.processingDuration, present: a.presentationDelay\n    },\n    visitId: VISIT_ID, route: ROUTE_PATTERN\n  });\n}\n\n[onLCP, onINP, onCLS, onTTFB].forEach((on) => on(record, { reportAllChanges: false }));\n\u002F\u002F trade-off: reportAllChanges:false reports one final value per metric, which is\n\u002F\u002F what percentile dashboards want. Turn it on only in a debugging build — it\n\u002F\u002F multiplies beacon volume and makes p75 aggregation ambiguous.\n","javascript","",[45,323,324,346,353,376,381,400,420,432,442,470,476,485,508,514,526,532,538,553,559,565,570,602,608,614],{"__ignoreMap":321},[325,326,328,332,336,339,343],"span",{"class":121,"line":327},1,[325,329,331],{"class":330},"sPARh","import",[325,333,335],{"class":334},"saISM"," { onLCP, onINP, onCLS, onTTFB } ",[325,337,338],{"class":330},"from",[325,340,342],{"class":341},"sZ8jY"," 'web-vitals\u002Fattribution'",[325,344,345],{"class":334},";\n",[325,347,349],{"class":121,"line":348},2,[325,350,352],{"emptyLinePlaceholder":351},true,"\n",[325,354,356,359,363,366,369,373],{"class":121,"line":355},3,[325,357,358],{"class":330},"const",[325,360,362],{"class":361},"sPXB4"," queue",[325,364,365],{"class":330}," =",[325,367,368],{"class":330}," new",[325,370,372],{"class":371},"smZ65"," Set",[325,374,375],{"class":334},"();\n",[325,377,379],{"class":121,"line":378},4,[325,380,352],{"emptyLinePlaceholder":351},[325,382,384,387,390,393,397],{"class":121,"line":383},5,[325,385,386],{"class":330},"function",[325,388,389],{"class":371}," record",[325,391,392],{"class":334},"(",[325,394,396],{"class":395},"sQw3B","metric",[325,398,399],{"class":334},") {\n",[325,401,403,406,409,411,414,417],{"class":121,"line":402},6,[325,404,405],{"class":330},"  const",[325,407,408],{"class":361}," a",[325,410,365],{"class":330},[325,412,413],{"class":334}," metric.attribution ",[325,415,416],{"class":330},"||",[325,418,419],{"class":334}," {};\n",[325,421,423,426,429],{"class":121,"line":422},7,[325,424,425],{"class":334},"  queue.",[325,427,428],{"class":371},"add",[325,430,431],{"class":334},"({\n",[325,433,435,438],{"class":121,"line":434},8,[325,436,437],{"class":334},"    name: metric.name,                       ",[325,439,441],{"class":440},"sjfSM","\u002F\u002F LCP | INP | CLS | TTFB\n",[325,443,445,448,451,454,457,460,463,465,467],{"class":121,"line":444},9,[325,446,447],{"class":334},"    value: Math.",[325,449,450],{"class":371},"round",[325,452,453],{"class":334},"(metric.value ",[325,455,456],{"class":330},"*",[325,458,459],{"class":361}," 1000",[325,461,462],{"class":334},") ",[325,464,247],{"class":330},[325,466,459],{"class":361},[325,468,469],{"class":334},",\n",[325,471,473],{"class":121,"line":472},10,[325,474,475],{"class":334},"    rating: metric.rating,\n",[325,477,479,482],{"class":121,"line":478},11,[325,480,481],{"class":334},"    navType: metric.navigationType,          ",[325,483,484],{"class":440},"\u002F\u002F navigate | back-forward-cache | reload\n",[325,486,488,491,493,496,498,501,503,506],{"class":121,"line":487},12,[325,489,490],{"class":334},"    target: a.interactionTarget ",[325,492,416],{"class":330},[325,494,495],{"class":334}," a.element ",[325,497,416],{"class":330},[325,499,500],{"class":334}," a.largestShiftTarget ",[325,502,416],{"class":330},[325,504,505],{"class":361}," null",[325,507,469],{"class":334},[325,509,511],{"class":121,"line":510},13,[325,512,513],{"class":334},"    phases: {\n",[325,515,517,520,523],{"class":121,"line":516},14,[325,518,519],{"class":334},"      ttfb: a.timeToFirstByte, delay: a.inputDelay ",[325,521,522],{"class":330},"??",[325,524,525],{"class":334}," a.resourceLoadDelay,\n",[325,527,529],{"class":121,"line":528},15,[325,530,531],{"class":334},"      process: a.processingDuration, present: a.presentationDelay\n",[325,533,535],{"class":121,"line":534},16,[325,536,537],{"class":334},"    },\n",[325,539,541,544,547,550],{"class":121,"line":540},17,[325,542,543],{"class":334},"    visitId: ",[325,545,546],{"class":361},"VISIT_ID",[325,548,549],{"class":334},", route: ",[325,551,552],{"class":361},"ROUTE_PATTERN\n",[325,554,556],{"class":121,"line":555},18,[325,557,558],{"class":334},"  });\n",[325,560,562],{"class":121,"line":561},19,[325,563,564],{"class":334},"}\n",[325,566,568],{"class":121,"line":567},20,[325,569,352],{"emptyLinePlaceholder":351},[325,571,573,576,579,582,585,587,590,593,596,599],{"class":121,"line":572},21,[325,574,575],{"class":334},"[onLCP, onINP, onCLS, onTTFB].",[325,577,578],{"class":371},"forEach",[325,580,581],{"class":334},"((",[325,583,584],{"class":395},"on",[325,586,462],{"class":334},[325,588,589],{"class":330},"=>",[325,591,592],{"class":371}," on",[325,594,595],{"class":334},"(record, { reportAllChanges: ",[325,597,598],{"class":361},"false",[325,600,601],{"class":334}," }));\n",[325,603,605],{"class":121,"line":604},22,[325,606,607],{"class":440},"\u002F\u002F trade-off: reportAllChanges:false reports one final value per metric, which is\n",[325,609,611],{"class":121,"line":610},23,[325,612,613],{"class":440},"\u002F\u002F what percentile dashboards want. Turn it on only in a debugging build — it\n",[325,615,617],{"class":121,"line":616},24,[325,618,619],{"class":440},"\u002F\u002F multiplies beacon volume and makes p75 aggregation ambiguous.\n",[15,621,284,622,625,626,40],{},[45,623,624],{},"target"," field is the single highest-value column in the whole schema. Once you can group failing INP by interaction target, the work stops being \"improve INP\" and becomes \"the filter dropdown's change handler costs 340ms on low-end Android\" — a bug with an owner. That grouping technique is worked through in ",[19,627,629],{"href":628},"\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002Fattributing-inp-to-a-component-with-the-attribution-build\u002F","attributing INP to a component",[226,631,633],{"id":632},"_2-deliver-the-beacon-without-losing-it","2. Deliver the Beacon Without Losing It",[15,635,636],{},"The hardest engineering problem in RUM is not measurement, it is delivery. INP and CLS are only final when the page is being unloaded, which is precisely the moment the browser stops honouring ordinary requests. Three rules keep loss rates under 1%.",[15,638,639,649,650,652,653,656,657,660,661,663],{},[240,640,641,642,645,646,40],{},"Flush on ",[45,643,644],{},"visibilitychange",", not ",[45,647,648],{},"unload"," The ",[45,651,648],{}," and ",[45,654,655],{},"beforeunload"," events do not fire reliably on mobile and disable the back\u002Fforward cache outright. The ",[45,658,659],{},"hidden"," transition of ",[45,662,644],{}," fires on tab switch, app switch and navigation away, on every platform.",[15,665,666,672,673,676,677,680,681,40],{},[240,667,668,669,40],{},"Use ",[45,670,671],{},"navigator.sendBeacon"," It queues the request in the browser process, so it survives the document being torn down, and it never blocks the unload path. Keep the payload under the ~64KB limit and fall back to ",[45,674,675],{},"fetch(..., { keepalive: true })"," where ",[45,678,679],{},"sendBeacon"," returns ",[45,682,598],{},[15,684,685,688],{},[240,686,687],{},"Batch, then flush once."," Sending four separate beacons per page view quadruples request volume for no analytical gain. Accumulate into a queue and flush the whole set together.",[316,690,692],{"className":318,"code":691,"language":320,"meta":321,"style":321},"function flush() {\n  if (!queue.size) return;\n  const body = JSON.stringify({ ts: Date.now(), metrics: [...queue] });\n  queue.clear();\n  const ok = navigator.sendBeacon('\u002Frum', new Blob([body], { type: 'application\u002Fjson' }));\n  if (!ok) fetch('\u002Frum', { body, method: 'POST', keepalive: true }).catch(() => {});\n}\n\naddEventListener('visibilitychange', () => {\n  if (document.visibilityState === 'hidden') flush();\n});\n\u002F\u002F trade-off: sendBeacon cannot report failures and gives you no response, so a\n\u002F\u002F broken collector is silently invisible. Monitor collector-side request volume\n\u002F\u002F against session counts, not client-side success, to detect delivery loss.\n",[45,693,694,704,723,755,764,799,843,847,851,869,889,894,899,904],{"__ignoreMap":321},[325,695,696,698,701],{"class":121,"line":327},[325,697,386],{"class":330},[325,699,700],{"class":371}," flush",[325,702,703],{"class":334},"() {\n",[325,705,706,709,712,715,718,721],{"class":121,"line":348},[325,707,708],{"class":330},"  if",[325,710,711],{"class":334}," (",[325,713,714],{"class":330},"!",[325,716,717],{"class":334},"queue.size) ",[325,719,720],{"class":330},"return",[325,722,345],{"class":334},[325,724,725,727,730,732,735,737,740,743,746,749,752],{"class":121,"line":355},[325,726,405],{"class":330},[325,728,729],{"class":361}," body",[325,731,365],{"class":330},[325,733,734],{"class":361}," JSON",[325,736,40],{"class":334},[325,738,739],{"class":371},"stringify",[325,741,742],{"class":334},"({ ts: Date.",[325,744,745],{"class":371},"now",[325,747,748],{"class":334},"(), metrics: [",[325,750,751],{"class":330},"...",[325,753,754],{"class":334},"queue] });\n",[325,756,757,759,762],{"class":121,"line":378},[325,758,425],{"class":334},[325,760,761],{"class":371},"clear",[325,763,375],{"class":334},[325,765,766,768,771,773,776,778,780,783,785,788,791,794,797],{"class":121,"line":383},[325,767,405],{"class":330},[325,769,770],{"class":361}," ok",[325,772,365],{"class":330},[325,774,775],{"class":334}," navigator.",[325,777,679],{"class":371},[325,779,392],{"class":334},[325,781,782],{"class":341},"'\u002Frum'",[325,784,32],{"class":334},[325,786,787],{"class":330},"new",[325,789,790],{"class":371}," Blob",[325,792,793],{"class":334},"([body], { type: ",[325,795,796],{"class":341},"'application\u002Fjson'",[325,798,601],{"class":334},[325,800,801,803,805,807,810,813,815,817,820,823,826,829,832,835,838,840],{"class":121,"line":402},[325,802,708],{"class":330},[325,804,711],{"class":334},[325,806,714],{"class":330},[325,808,809],{"class":334},"ok) ",[325,811,812],{"class":371},"fetch",[325,814,392],{"class":334},[325,816,782],{"class":341},[325,818,819],{"class":334},", { body, method: ",[325,821,822],{"class":341},"'POST'",[325,824,825],{"class":334},", keepalive: ",[325,827,828],{"class":361},"true",[325,830,831],{"class":334}," }).",[325,833,834],{"class":371},"catch",[325,836,837],{"class":334},"(() ",[325,839,589],{"class":330},[325,841,842],{"class":334}," {});\n",[325,844,845],{"class":121,"line":422},[325,846,564],{"class":334},[325,848,849],{"class":121,"line":434},[325,850,352],{"emptyLinePlaceholder":351},[325,852,853,856,858,861,864,866],{"class":121,"line":444},[325,854,855],{"class":371},"addEventListener",[325,857,392],{"class":334},[325,859,860],{"class":341},"'visibilitychange'",[325,862,863],{"class":334},", () ",[325,865,589],{"class":330},[325,867,868],{"class":334}," {\n",[325,870,871,873,876,879,882,884,887],{"class":121,"line":472},[325,872,708],{"class":330},[325,874,875],{"class":334}," (document.visibilityState ",[325,877,878],{"class":330},"===",[325,880,881],{"class":341}," 'hidden'",[325,883,462],{"class":334},[325,885,886],{"class":371},"flush",[325,888,375],{"class":334},[325,890,891],{"class":121,"line":478},[325,892,893],{"class":334},"});\n",[325,895,896],{"class":121,"line":487},[325,897,898],{"class":440},"\u002F\u002F trade-off: sendBeacon cannot report failures and gives you no response, so a\n",[325,900,901],{"class":121,"line":510},[325,902,903],{"class":440},"\u002F\u002F broken collector is silently invisible. Monitor collector-side request volume\n",[325,905,906],{"class":121,"line":516},[325,907,908],{"class":440},"\u002F\u002F against session counts, not client-side success, to detect delivery loss.\n",[15,910,911,912,915,916,40],{},"The full failure taxonomy for this step — including why a beacon sent from a ",[45,913,914],{},"pagehide"," handler in a single-page app can double-count — is covered in ",[19,917,919],{"href":918},"\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",[226,921,923],{"id":922},"_3-choose-a-sampling-rate-you-can-defend","3. Choose a Sampling Rate You Can Defend",[15,925,926],{},"Sampling is a statistics decision disguised as a cost decision. The question is not \"how much data can we afford?\" but \"how small a segment do we need to be able to see?\".",[58,928,66,931,74,934,74,937,74,940,74,943,74,947,74,952,74,958,74,962,74,966,74,971,74,977,74,981,74,985,74,988,74,991,74,995,74,998,74,1001,74,1005,74,1008,74,1011,74,1014,74,1020,74,1024,74,1027,74],{"xmlns":60,"viewBox":929,"width":62,"role":63,"ariaLabel":930,"style":65},"0 0 760 268","Chart comparing daily beacon volume against the smallest segment that stays statistically visible at sampling rates of 100 percent, 25 percent, 10 percent and 1 percent",[68,932],{"className":933,"x":72,"y":72,"width":62,"height":62,"fill":73},[71],[76,935,936],{},"Sampling rate versus the smallest segment you can still see",[80,938,939],{},"Four sampling rates plotted against the number of daily samples they yield for a one million visit site, and the smallest route or device segment that still clears a thousand samples per day.",[68,941],{"x":85,"y":85,"width":86,"height":942,"rx":88,"fill":89,"stroke":90,"style":91},"266",[93,944,946],{"x":95,"y":945,"fill":90,"style":97},"34","Sampling rate versus segment resolution",[93,948,951],{"x":95,"y":949,"fill":90,"style":950},"56","font-size:11.5px;fill-opacity:0.75","Site with 1,000,000 visits\u002Fday; a segment needs ≈1,000 samples\u002Fday for a stable p75",[93,953,957],{"x":954,"y":955,"fill":90,"style":956,"textAnchor":113},"90","86","font-size:12px;font-weight:700;","rate",[93,959,961],{"x":960,"y":955,"fill":90,"style":956,"textAnchor":113},"300","samples\u002Fday",[93,963,965],{"x":964,"y":955,"fill":90,"style":956,"textAnchor":113},"600","smallest visible segment",[121,967],{"x1":95,"y1":968,"x2":969,"y2":968,"stroke":90,"style":970},"96","736","stroke-opacity:0.3",[68,972],{"x":103,"y":973,"width":960,"height":974,"rx":975,"fill":106,"stroke":106,"style":976},"106","26","4","fill-opacity:0.16;stroke-opacity:0.5",[93,978,62],{"x":954,"y":979,"fill":90,"style":980,"textAnchor":113},"124","font-size:12px;",[93,982,984],{"x":960,"y":979,"fill":90,"style":983,"textAnchor":113},"font-size:11.5px;","1,000,000",[93,986,987],{"x":964,"y":979,"fill":90,"style":983,"textAnchor":113},"0.1% of traffic — any route",[68,989],{"x":103,"y":990,"width":103,"height":974,"rx":975,"fill":106,"stroke":106,"style":976},"144",[93,992,994],{"x":954,"y":993,"fill":90,"style":980,"textAnchor":113},"162","25%",[93,996,997],{"x":960,"y":993,"fill":90,"style":983,"textAnchor":113},"250,000",[93,999,1000],{"x":964,"y":993,"fill":90,"style":983,"textAnchor":113},"0.4% — route × device class",[68,1002],{"x":103,"y":1003,"width":1004,"height":974,"rx":975,"fill":153,"stroke":154,"style":155},"182","60",[93,1006,1007],{"x":954,"y":205,"fill":90,"style":980,"textAnchor":113},"10%",[93,1009,1010],{"x":960,"y":205,"fill":90,"style":983,"textAnchor":113},"100,000",[93,1012,1013],{"x":964,"y":205,"fill":90,"style":983,"textAnchor":113},"1% — route only",[68,1015],{"x":103,"y":1016,"width":1017,"height":974,"rx":1018,"fill":90,"stroke":90,"style":1019},"220","6","3","fill-opacity:0.35;stroke-opacity:0.5",[93,1021,1023],{"x":954,"y":1022,"fill":90,"style":980,"textAnchor":113},"238","1%",[93,1025,1026],{"x":960,"y":1022,"fill":90,"style":983,"textAnchor":113},"10,000",[93,1028,1029],{"x":964,"y":1022,"fill":90,"style":983,"textAnchor":113},"10% — site-wide trend only",[15,1031,1032,1033,1036],{},"Sample by ",[240,1034,1035],{},"visit",", never by beacon: a rate applied per metric produces page views where LCP was recorded and INP was not, and any join across metrics silently loses rows. Decide once at session start, stamp the decision into the visit ID, and let every beacon from that visit inherit it.",[15,1038,1039],{},"Keep the rate stable over time, and store it. When you later raise the rate from 10% to 25% to investigate a regression, historical comparisons remain valid only if each row knows which rate produced it.",[226,1041,1043],{"id":1042},"deconstructing-the-numbers-why-your-p75-and-crux-disagree","Deconstructing the Numbers: Why Your p75 and CrUX Disagree",[15,1045,1046,1047,1050,1051,1054],{},"The first serious question every RUM rollout faces is why the in-house dashboard reads ",[45,1048,1049],{},"2.1s"," while the Chrome UX Report says ",[45,1052,1053],{},"2.6s",". Both are usually correct; they are measuring different populations.",[234,1056,1057,1063,1069,1075],{},[237,1058,1059,1062],{},[240,1060,1061],{},"Browser coverage."," CrUX contains Chrome only, on visits from opted-in users. Your beacon fires in every browser that supports the APIs — and Safari reports no INP at all, so an iOS-heavy audience quietly removes the slowest population from one metric and not the others.",[237,1064,1065,1068],{},[240,1066,1067],{},"Aggregation window."," CrUX publishes a rolling 28-day distribution. A dashboard showing yesterday's p75 will always look different, especially after a deploy, and neither number is wrong.",[237,1070,1071,1074],{},[240,1072,1073],{},"The unit being aggregated."," CrUX aggregates over origin or URL groups with its own weighting; your query aggregates rows you collected. If a single high-traffic route dominates your table but is grouped separately in CrUX, the two p75s are not comparable at all.",[237,1076,1077,1080],{},[240,1078,1079],{},"Delivery loss."," Beacons that never arrive are disproportionately from the slowest sessions — the ones that closed the tab in frustration. Loss biases your p75 optimistic, which is the most dangerous direction.",[15,1082,1083,1084,40],{},"The reconciliation procedure, including how to build a Chrome-only, 28-day, origin-level view of your own data for an apples-to-apples check, is in ",[19,1085,1087],{"href":1086},"\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002Fcrux-vs-your-own-rum-why-the-numbers-differ\u002F","CrUX vs your own RUM",[226,1089,1091],{"id":1090},"advanced-diagnostics","Advanced Diagnostics",[15,1093,1094,1097,1098,1101],{},[240,1095,1096],{},"Back\u002Fforward-cache restores need their own bucket."," A bfcache restore produces a near-zero LCP and an INP that reflects only post-restore interactions. Mixed into cold loads, these entries drag the p75 down and hide real regressions. Filter on ",[45,1099,1100],{},"navigationType === 'back-forward-cache'"," and report it as a separate series.",[15,1103,1104,1107,1108,1111],{},[240,1105,1106],{},"Single-page navigations are not page views."," In an SPA, ",[45,1109,1110],{},"onLCP"," fires once for the hard load; subsequent route changes produce no new LCP. Reporting a soft navigation as a page view therefore under-counts LCP relative to visits. Either scope the metric to hard loads only or adopt the soft-navigation API where it is available, but do not silently mix the two.",[15,1113,1114,1117],{},[240,1115,1116],{},"Long sessions truncate INP."," INP updates as the visit continues, so a 40-minute session's final value is reported only at the end. If your users keep tabs open for hours, add a periodic flush — every few minutes of visible time — so a browser crash does not lose the whole session.",[15,1119,1120,1123],{},[240,1121,1122],{},"Consent gating changes the population."," If beacons only fire after a cookie banner is accepted, your data describes consenting users, who skew toward repeat visitors with warm caches. Where policy allows, send Core Web Vitals as anonymous aggregate telemetry outside the consent gate; where it does not, document the bias so nobody compares the number to CrUX without adjusting.",[226,1125,1127],{"id":1126},"validation-budgeting-and-alerting","Validation, Budgeting and Alerting",[15,1129,1130],{},"Treat the pipeline itself as a system that can fail silently, and monitor it accordingly.",[316,1132,1134],{"className":318,"code":1133,"language":320,"meta":321,"style":321},"\u002F\u002F Collector-side sanity checks, evaluated hourly.\n\u002F\u002F 1. Delivery ratio: beacons should track sessions within a stable band.\n\u002F\u002F    A sudden drop means a client-side error, not a performance win.\n\u002F\u002F 2. Metric completeness: every visit that reported LCP should eventually\n\u002F\u002F    report CLS. A widening gap means the flush is being missed on unload.\n\u002F\u002F 3. Cardinality guard: reject rows whose route pattern is not in the known\n\u002F\u002F    set, so a bad release cannot explode storage with raw URLs.\n\u002F\u002F trade-off: strict rejection loses data during a legitimate route launch.\n\u002F\u002F Log rejects to a side table for a week rather than discarding them.\n",[45,1135,1136,1141,1146,1151,1156,1161,1166,1171,1176],{"__ignoreMap":321},[325,1137,1138],{"class":121,"line":327},[325,1139,1140],{"class":440},"\u002F\u002F Collector-side sanity checks, evaluated hourly.\n",[325,1142,1143],{"class":121,"line":348},[325,1144,1145],{"class":440},"\u002F\u002F 1. Delivery ratio: beacons should track sessions within a stable band.\n",[325,1147,1148],{"class":121,"line":355},[325,1149,1150],{"class":440},"\u002F\u002F    A sudden drop means a client-side error, not a performance win.\n",[325,1152,1153],{"class":121,"line":378},[325,1154,1155],{"class":440},"\u002F\u002F 2. Metric completeness: every visit that reported LCP should eventually\n",[325,1157,1158],{"class":121,"line":383},[325,1159,1160],{"class":440},"\u002F\u002F    report CLS. A widening gap means the flush is being missed on unload.\n",[325,1162,1163],{"class":121,"line":402},[325,1164,1165],{"class":440},"\u002F\u002F 3. Cardinality guard: reject rows whose route pattern is not in the known\n",[325,1167,1168],{"class":121,"line":422},[325,1169,1170],{"class":440},"\u002F\u002F    set, so a bad release cannot explode storage with raw URLs.\n",[325,1172,1173],{"class":121,"line":434},[325,1174,1175],{"class":440},"\u002F\u002F trade-off: strict rejection loses data during a legitimate route launch.\n",[325,1177,1178],{"class":121,"line":444},[325,1179,1180],{"class":440},"\u002F\u002F Log rejects to a side table for a week rather than discarding them.\n",[15,1182,1183,1184,1187,1188,1191],{},"For alerting, compare a rolling 24-hour p75 against the same window from the previous week, per route, and alert on a relative regression rather than an absolute threshold crossing. Absolute thresholds fire constantly for routes that sit near the boundary and never fire for a route that degrades from ",[45,1185,1186],{},"0.8s"," to ",[45,1189,1190],{},"2.2s"," while still technically passing.",[15,1193,1194,1195,1199],{},"Pair field alerting with the lab assertions described in ",[19,1196,1198],{"href":1197},"\u002Fcore-web-vitals-measurement\u002Funderstanding-core-web-vitals-thresholds\u002Fbest-lighthouse-ci-setup-for-frontend-pipelines\u002F","the best Lighthouse CI setup for frontend pipelines",". CI stops a regression before it ships; RUM catches the regressions CI cannot model — a third-party tag change, a new device mix, a CDN incident.",[226,1201,1203],{"id":1202},"faq","FAQ",[1205,1206,1209,1213],"details",{"className":1207},[1208],"faq-item",[1210,1211,1212],"summary",{},"Do I need a RUM pipeline if I already have CrUX?",[15,1214,1215],{},"Yes, if you intend to fix things rather than only report them. CrUX gives you a trustworthy, comparable p75 but no attribution: it cannot tell you which element was the LCP candidate or which handler blew the INP budget, and it only covers Chrome traffic at origin or popular-URL granularity. Your own beacons add the element selectors, route patterns and device segments that turn a failing metric into an assigned ticket. Use CrUX as the scoreboard and your RUM as the diagnostic.",[1205,1217,1219,1222],{"className":1218},[1208],[1210,1220,1221],{},"What sampling rate should I start with?",[15,1223,1224],{},"Start at 100% for the first two weeks so you can see the real distribution and the real cost, then reduce until the smallest segment you care about still collects roughly a thousand samples a day. For most sites that lands between 10% and 25%. Sample per visit rather than per beacon, and store the rate on every row so historical comparisons stay valid when you change it.",[1205,1226,1228,1231],{"className":1227},[1208],[1210,1229,1230],{},"Why do some page views report LCP but never INP?",[15,1232,1233],{},"Because the user never interacted, or because the browser does not support the metric. INP requires at least one qualifying interaction, so bounce visits legitimately have none, and Safari does not report it at all. Treat missing INP as absent rather than zero — averaging zeros into the distribution makes a failing p75 look passing.",[1205,1235,1237,1240],{"className":1236},[1208],[1210,1238,1239],{},"Can the beacon itself hurt performance?",[15,1241,1242,1243,1245],{},"Only if you get the delivery mechanism wrong. The observers are passive and cost microseconds. The risks are a synchronous XHR on unload, a large JSON payload serialised on the main thread during the hide transition, and a collector endpoint on a cold third-party origin that forces a DNS lookup and TLS handshake at the worst possible moment. Use ",[45,1244,679],{},", keep the payload small, and host the collector on an origin the page already connected to.",[226,1247,1249],{"id":1248},"related","Related",[234,1251,1252,1259,1266,1272,1278,1283],{},[237,1253,1254,1258],{},[19,1255,1257],{"href":1256},"\u002Fcore-web-vitals-measurement\u002Funderstanding-core-web-vitals-thresholds\u002F","Understanding Core Web Vitals Thresholds"," — what the p75 boundary means before you start measuring it.",[237,1260,1261,1265],{},[19,1262,1264],{"href":1263},"\u002Fcore-web-vitals-measurement\u002Fprofiling-event-handlers-for-inp\u002F","Profiling Event Handlers for INP"," — take an attributed interaction target from the field and reproduce it in the Performance panel.",[237,1267,1268,1271],{},[19,1269,1270],{"href":918},"Sending Web Vitals with sendBeacon on visibility change"," — the delivery details that decide whether your data arrives at all.",[237,1273,1274,1277],{},[19,1275,1276],{"href":628},"Attributing INP to a component"," — map an interaction target back to the component that owns it.",[237,1279,1280,1282],{},[19,1281,1087],{"href":1086}," — reconcile two correct numbers that disagree.",[237,1284,1285,1289],{},[19,1286,1288],{"href":1287},"\u002Fcore-web-vitals-measurement\u002Funderstanding-core-web-vitals-thresholds\u002Fwhy-lab-and-field-lcp-disagree\u002F","Why lab and field LCP disagree"," — the synthetic-versus-real gap on a single metric.",[1291,1292,1294],"script",{"type":1293},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Build a Core Web Vitals RUM pipeline\",\n  \"description\": \"Instrument LCP, INP and CLS with the web-vitals attribution build, deliver batched beacons reliably on page hide, choose a defensible sampling rate, and aggregate into p75 dashboards with regression alerting.\",\n  \"step\": [\n    { \"@type\": \"HowToStep\", \"position\": 1, \"name\": \"Instrument with attribution\", \"text\": \"Register onLCP, onINP, onCLS and onTTFB from the web-vitals attribution build and capture the element or interaction target with each value.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002F#1-instrument-the-metrics-with-attribution\" },\n    { \"@type\": \"HowToStep\", \"position\": 2, \"name\": \"Deliver the beacon\", \"text\": \"Batch the metrics and flush once with navigator.sendBeacon on the visibilitychange hidden transition.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002F#2-deliver-the-beacon-without-losing-it\" },\n    { \"@type\": \"HowToStep\", \"position\": 3, \"name\": \"Choose a sampling rate\", \"text\": \"Sample per visit at a rate that keeps the smallest segment you care about above roughly a thousand samples per day, and store the rate on every row.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002F#3-choose-a-sampling-rate-you-can-defend\" },\n    { \"@type\": \"HowToStep\", \"position\": 4, \"name\": \"Reconcile against CrUX\", \"text\": \"Account for browser coverage, the 28-day window, aggregation unit and delivery loss before treating a difference as a bug.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002F#deconstructing-the-numbers-why-your-p75-and-crux-disagree\" },\n    { \"@type\": \"HowToStep\", \"position\": 5, \"name\": \"Alert on relative regression\", \"text\": \"Compare a rolling 24-hour p75 per route against the same window a week earlier and alert on relative movement rather than threshold crossings.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002F#validation-budgeting-and-alerting\" }\n  ]\n}\n",[1291,1296,1297],{"type":1293},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"RUM Beacons & Field Data Collection: Turning p75 Into an Actionable Signal\",\n  \"description\": \"How to instrument Core Web Vitals in production with attribution-grade beacons, reliable delivery, defensible sampling and percentile dashboards.\",\n  \"datePublished\": \"2026-08-01\",\n  \"dateModified\": \"2026-08-01\",\n  \"author\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"publisher\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"mainEntityOfPage\": { \"@type\": \"WebPage\", \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002F\" }\n}\n",[1291,1299,1300],{"type":1293},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    { \"@type\": \"Question\", \"name\": \"Do I need a RUM pipeline if I already have CrUX?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"Yes, if you intend to fix things rather than only report them. CrUX gives a comparable p75 but no attribution, covers Chrome only, and aggregates at origin or popular-URL level. Your own beacons add element selectors, route patterns and device segments.\" } },\n    { \"@type\": \"Question\", \"name\": \"What sampling rate should I start with?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"Start at 100% for two weeks to see the real distribution and cost, then reduce until the smallest segment you care about still collects about a thousand samples a day — typically 10% to 25%. Sample per visit, not per beacon, and store the rate on every row.\" } },\n    { \"@type\": \"Question\", \"name\": \"Why do some page views report LCP but never INP?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"INP requires at least one qualifying interaction, so bounce visits have none, and Safari does not report the metric at all. Treat missing INP as absent rather than zero, because averaging zeros makes a failing p75 look passing.\" } },\n    { \"@type\": \"Question\", \"name\": \"Can the beacon itself hurt performance?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"Only with the wrong delivery mechanism. Avoid synchronous requests on unload, large payloads serialised during the hide transition, and a collector on a cold third-party origin. Use sendBeacon with a small payload on an origin the page already connected to.\" } }\n  ]\n}\n",[1291,1302,1303],{"type":1293},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"Core Web Vitals & Measurement\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"RUM Beacons & Field Data Collection\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002F\" }\n  ]\n}\n",[1305,1306,1307],"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":321,"searchDepth":348,"depth":348,"links":1309},[1310,1311,1312,1313,1314,1315,1316,1317,1318,1319],{"id":228,"depth":348,"text":229},{"id":278,"depth":348,"text":279},{"id":310,"depth":348,"text":311},{"id":632,"depth":348,"text":633},{"id":922,"depth":348,"text":923},{"id":1042,"depth":348,"text":1043},{"id":1090,"depth":348,"text":1091},{"id":1126,"depth":348,"text":1127},{"id":1202,"depth":348,"text":1203},{"id":1248,"depth":348,"text":1249},"Instrument Core Web Vitals in production: attribution-grade beacons, reliable delivery on page hide, sampling strategy, and percentile dashboards engineers can act on.","md",{"slug":1323,"type":1324,"breadcrumb":1325,"datePublished":1331,"dateModified":1331},"rum-beacons-and-field-data-collection","topic",[1326,1328,1329],{"name":1327,"url":247},"Home",{"name":22,"url":21},{"name":5,"url":1330},"\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002F","2026-08-01","\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection",{"title":1334,"description":1335},"RUM Beacons & Field Data Collection for Core Web Vitals","Build a real-user monitoring pipeline for LCP, INP and CLS: web-vitals attribution, sendBeacon batching, sampling, dimensions, and a p75 dashboard that matches CrUX.","core-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002Findex","sGikuZ1XqkgECigA4iEyj8EP5DWKj3Qwks-Xx6A9S-0",[1339,1343],{"title":1340,"path":1341,"stem":1342,"children":-1},"Reserving Space for Embeds with aspect-ratio","\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Freserving-space-for-embeds-with-aspect-ratio","core-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Freserving-space-for-embeds-with-aspect-ratio\u002Findex",{"title":1344,"path":1345,"stem":1346,"children":-1},"Attributing INP to a Component","\u002Fcore-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002Fattributing-inp-to-a-component-with-the-attribution-build","core-web-vitals-measurement\u002Frum-beacons-and-field-data-collection\u002Fattributing-inp-to-a-component-with-the-attribution-build\u002Findex",1785622405979]