[{"data":1,"prerenderedAt":1339},["ShallowReactive",2],{"blog-ai-coding-agents-core-web-vitals/":3},{"id":4,"title":5,"body":6,"description":1285,"extension":1286,"faqs":1287,"image":1303,"imageAlt":1304,"imageCaption":1305,"meta":1306,"navigation":291,"path":1307,"publishedAt":1308,"readingTime":1309,"references":1310,"seo":1329,"stem":1330,"tags":1331,"updatedAt":1308,"__hash__":1338},"blog/blog/ai-coding-agents-core-web-vitals.md","AI Coding Agents Can Hurt Core Web Vitals: A Frontend Review Checklist",{"type":7,"value":8,"toc":1259},"minimark",[9,13,22,25,30,33,86,89,93,96,99,120,123,129,133,136,139,166,169,204,207,210,230,242,245,253,257,260,271,274,385,388,391,419,422,442,444,450,454,461,464,487,490,493,551,558,560,566,570,577,580,606,609,702,705,708,728,730,736,740,747,750,781,784,786,792,796,799,825,828,831,851,859,863,866,869,892,895,898,904,907,911,914,917,923,926,930,933,936,953,956,960,963,968,998,1002,1026,1030,1060,1064,1091,1095,1116,1120,1141,1145,1172,1176,1179,1185,1189,1192,1198,1202,1205,1208,1225,1228,1236,1240,1243,1249,1252,1255],[10,11,12],"p",{},"AI coding agents are now good enough to change real production code. That is useful. It is also exactly why they can quietly make a fast frontend slower.",[10,14,15,16,21],{},"A Claude Code, Codex, Copilot, or Cursor-style agent might solve the ticket perfectly and still hurt ",[17,18,20],"a",{"href":19},"/blog/ai-fix-core-web-vitals/","Core Web Vitals",". It can add a chart library for one small widget. It can turn a server-rendered component into a client component because that was easier. It can lazy-load the hero image. It can add a neat animation that blocks input. The PR passes tests, the UI looks fine, and the regression only shows up later in Lighthouse or field data.",[10,23,24],{},"This article is a practical review checklist for AI-generated frontend changes. Use it before merging agent PRs in React, Next.js, Nuxt, Astro, or any JavaScript-heavy app.",[26,27,29],"h2",{"id":28},"the-short-version","The short version",[10,31,32],{},"Before merging an AI-generated frontend PR, check:",[34,35,36,44,50,56,62,68,74,80],"ol",{},[37,38,39,43],"li",{},[40,41,42],"strong",{},"Dependencies:"," did the agent add a package for something you could do with CSS or a small helper?",[37,45,46,49],{},[40,47,48],{},"Client JavaScript:"," did it move code from server/static rendering into the browser?",[37,51,52,55],{},[40,53,54],{},"LCP:"," did it change the hero image, font loading, route shell, or above-the-fold data?",[37,57,58,61],{},[40,59,60],{},"INP:"," did it add expensive event handlers, effects, animations, or hydration work?",[37,63,64,67],{},[40,65,66],{},"CLS:"," did it remove image dimensions, skeleton sizes, reserved ad space, or stable layout containers?",[37,69,70,73],{},[40,71,72],{},"Third parties:"," did it add analytics, chat, embeds, maps, video, or tag-manager code?",[37,75,76,79],{},[40,77,78],{},"Bundle output:"," did the first-load JavaScript or route chunk size grow?",[37,81,82,85],{},[40,83,84],{},"Measurements:"," did the agent provide before/after build and Lighthouse evidence?",[10,87,88],{},"If the answer is “I’m not sure” to any of these, do not merge yet. Ask the agent to inspect and report back.",[26,90,92],{"id":91},"why-ai-generated-code-can-be-performance-risky","Why AI-generated code can be performance-risky",[10,94,95],{},"AI agents optimise for completing the requested task. Unless you explicitly tell them otherwise, they are not necessarily optimising for runtime performance.",[10,97,98],{},"A human frontend engineer carries a lot of context in their head:",[100,101,102,105,108,111,114,117],"ul",{},[37,103,104],{},"this route is already close to the JavaScript budget",[37,106,107],{},"this component must stay server-rendered",[37,109,110],{},"this hero image is the LCP candidate",[37,112,113],{},"this carousel is intentionally CSS-only",[37,115,116],{},"this ad slot has reserved height because CLS used to be bad",[37,118,119],{},"this library was removed last month because it bloated the bundle",[10,121,122],{},"An agent may not know any of that. Even when it reads the codebase, it can miss the reason a boring-looking pattern exists.",[10,124,125,126],{},"That is the core risk: ",[40,127,128],{},"AI agents often produce plausible code without understanding the performance scars behind the current code.",[26,130,132],{"id":131},"_1-check-new-dependencies-first","1. Check new dependencies first",[10,134,135],{},"Start every AI PR review with the package diff.",[10,137,138],{},"Look for changes in:",[100,140,141,147,160,163],{},[37,142,143],{},[144,145,146],"code",{},"package.json",[37,148,149,152,153,156,157],{},[144,150,151],{},"package-lock.json",", ",[144,154,155],{},"pnpm-lock.yaml",", or ",[144,158,159],{},"yarn.lock",[37,161,162],{},"framework config files",[37,164,165],{},"route-level imports",[10,167,168],{},"Common agent behaviour:",[170,171,176],"pre",{"className":172,"code":173,"language":174,"meta":175,"style":175},"language-diff shiki shiki-themes github-light github-dark","+ import { motion } from 'framer-motion'\n+ import dayjs from 'dayjs'\n+ import { LineChart } from 'recharts'\n+ import debounce from 'lodash/debounce'\n","diff","",[144,177,178,186,192,198],{"__ignoreMap":175},[179,180,183],"span",{"class":181,"line":182},"line",1,[179,184,185],{},"+ import { motion } from 'framer-motion'\n",[179,187,189],{"class":181,"line":188},2,[179,190,191],{},"+ import dayjs from 'dayjs'\n",[179,193,195],{"class":181,"line":194},3,[179,196,197],{},"+ import { LineChart } from 'recharts'\n",[179,199,201],{"class":181,"line":200},4,[179,202,203],{},"+ import debounce from 'lodash/debounce'\n",[10,205,206],{},"Sometimes that is fine. Often it is not.",[10,208,209],{},"A single agent-generated feature can pull in:",[100,211,212,215,218,221,224,227],{},[37,213,214],{},"animation libraries for a hover effect",[37,216,217],{},"chart libraries for a simple sparkline",[37,219,220],{},"date libraries for formatting one date",[37,222,223],{},"icon packs for three icons",[37,225,226],{},"markdown/rendering libraries for static copy",[37,228,229],{},"validation libraries for a tiny form",[10,231,232,233,236,237,241],{},"For Next.js and React apps, compare the output from your production build. If you use Next.js, the ",[144,234,235],{},"First Load JS"," number is the first thing to check. See the ",[17,238,240],{"href":239},"/blog/nextjs-bundle-size/","Next.js build output guide"," if that table is confusing.",[10,243,244],{},"Useful review prompt:",[170,246,251],{"className":247,"code":249,"language":250,"meta":175},[248],"language-text","Review this PR for new dependencies and bundle-size risk.\nFor each new package, explain:\n1. where it is imported,\n2. whether it affects initial load or only a lazy route,\n3. whether native browser APIs, CSS, or existing project utilities could replace it,\n4. the expected impact on first-load JavaScript.\nDo not change code yet. Return a short risk table.\n","text",[144,252,249],{"__ignoreMap":175},[26,254,256],{"id":255},"_2-check-whether-server-rendered-code-became-client-javascript","2. Check whether server-rendered code became client JavaScript",[10,258,259],{},"This is one of the easiest ways for an AI agent to make a modern app slower.",[10,261,262,263,266,267,270],{},"In Next.js App Router, watch for added ",[144,264,265],{},"\"use client\""," directives. In Astro, watch for extra ",[144,268,269],{},"client:load"," islands. In Nuxt, watch for browser-only components, large composables, or plugins that now run on every page.",[10,272,273],{},"The agent might add client-side state because it is the fastest way to make a component interactive:",[170,275,279],{"className":276,"code":277,"language":278,"meta":175,"style":175},"language-tsx shiki shiki-themes github-light github-dark","'use client'\n\nimport { useState } from 'react'\n\nexport function PricingCards({ plans }) {\n  const [selected, setSelected] = useState(plans[0].id)\n  // ...\n}\n","tsx",[144,280,281,287,293,309,313,336,372,379],{"__ignoreMap":175},[179,282,283],{"class":181,"line":182},[179,284,286],{"class":285},"sZZnC","'use client'\n",[179,288,289],{"class":181,"line":188},[179,290,292],{"emptyLinePlaceholder":291},true,"\n",[179,294,295,299,303,306],{"class":181,"line":194},[179,296,298],{"class":297},"szBVR","import",[179,300,302],{"class":301},"sVt8B"," { useState } ",[179,304,305],{"class":297},"from",[179,307,308],{"class":285}," 'react'\n",[179,310,311],{"class":181,"line":200},[179,312,292],{"emptyLinePlaceholder":291},[179,314,316,319,322,326,329,333],{"class":181,"line":315},5,[179,317,318],{"class":297},"export",[179,320,321],{"class":297}," function",[179,323,325],{"class":324},"sScJk"," PricingCards",[179,327,328],{"class":301},"({ ",[179,330,332],{"class":331},"s4XuR","plans",[179,334,335],{"class":301}," }) {\n",[179,337,339,342,345,349,351,354,357,360,363,366,369],{"class":181,"line":338},6,[179,340,341],{"class":297},"  const",[179,343,344],{"class":301}," [",[179,346,348],{"class":347},"sj4cs","selected",[179,350,152],{"class":301},[179,352,353],{"class":347},"setSelected",[179,355,356],{"class":301},"] ",[179,358,359],{"class":297},"=",[179,361,362],{"class":324}," useState",[179,364,365],{"class":301},"(plans[",[179,367,368],{"class":347},"0",[179,370,371],{"class":301},"].id)\n",[179,373,375],{"class":181,"line":374},7,[179,376,378],{"class":377},"sJ8bj","  // ...\n",[179,380,382],{"class":181,"line":381},8,[179,383,384],{"class":301},"}\n",[10,386,387],{},"That may be acceptable for a genuinely interactive component. It is not acceptable if the whole page became a client component just to toggle one small detail.",[10,389,390],{},"Review questions:",[100,392,393,396,399,402,416],{},[37,394,395],{},"Did a parent layout become a client component?",[37,397,398],{},"Did a server component start importing client-only code?",[37,400,401],{},"Did static content become hydrated JavaScript?",[37,403,404,405,408,409,412,413,415],{},"Did an Astro island change from ",[144,406,407],{},"client:visible"," or ",[144,410,411],{},"client:idle"," to ",[144,414,269],{},"?",[37,417,418],{},"Did a Nuxt plugin move from route-specific usage to global usage?",[10,420,421],{},"Related guides:",[100,423,424,430,436],{},[37,425,426],{},[17,427,429],{"href":428},"/blog/react-server-components-core-web-vitals/","React Server Components and Core Web Vitals",[37,431,432],{},[17,433,435],{"href":434},"/blog/astro-performance-optimization/","Astro performance optimization",[37,437,438],{},[17,439,441],{"href":440},"/blog/nuxt-performance-optimization/","Nuxt performance optimization",[10,443,244],{},[170,445,448],{"className":446,"code":447,"language":250,"meta":175},[248],"Check whether this change increases client-side JavaScript.\nLook for new \"use client\" boundaries, hydrated islands, browser-only plugins, or components that now run on every page.\nSuggest the smallest change that keeps static/server-rendered code out of the client bundle.\n",[144,449,447],{"__ignoreMap":175},[26,451,453],{"id":452},"_3-protect-the-lcp-element","3. Protect the LCP element",[10,455,456,460],{},[17,457,459],{"href":458},"/blog/how-to-improve-lcp/","Largest Contentful Paint"," is often controlled by one obvious thing: the hero image, heading block, or top-of-page content.",[10,462,463],{},"AI agents commonly break LCP by:",[100,465,466,469,472,475,478,481,484],{},[37,467,468],{},"lazy-loading the hero image",[37,470,471],{},"removing image priority hints",[37,473,474],{},"changing image dimensions",[37,476,477],{},"adding a client-rendered hero carousel",[37,479,480],{},"moving critical content behind a loading state",[37,482,483],{},"adding a font or animation that delays above-the-fold text",[37,485,486],{},"fetching hero content on the client instead of rendering it in HTML",[10,488,489],{},"A visually nicer hero can be a worse hero.",[10,491,492],{},"Bad sign:",[170,494,496],{"className":276,"code":495,"language":278,"meta":175,"style":175},"\u003CImage\n  src={hero.src}\n  alt={hero.alt}\n  loading=\"lazy\"\n  className=\"rounded-3xl\"\n/>\n",[144,497,498,506,516,526,536,546],{"__ignoreMap":175},[179,499,500,503],{"class":181,"line":182},[179,501,502],{"class":301},"\u003C",[179,504,505],{"class":347},"Image\n",[179,507,508,511,513],{"class":181,"line":188},[179,509,510],{"class":324},"  src",[179,512,359],{"class":297},[179,514,515],{"class":301},"{hero.src}\n",[179,517,518,521,523],{"class":181,"line":194},[179,519,520],{"class":324},"  alt",[179,522,359],{"class":297},[179,524,525],{"class":301},"{hero.alt}\n",[179,527,528,531,533],{"class":181,"line":200},[179,529,530],{"class":324},"  loading",[179,532,359],{"class":297},[179,534,535],{"class":285},"\"lazy\"\n",[179,537,538,541,543],{"class":181,"line":315},[179,539,540],{"class":324},"  className",[179,542,359],{"class":297},[179,544,545],{"class":285},"\"rounded-3xl\"\n",[179,547,548],{"class":181,"line":338},[179,549,550],{"class":301},"/>\n",[10,552,553,554,557],{},"If that image is the LCP candidate, lazy loading is probably wrong. Give it stable dimensions, appropriate ",[144,555,556],{},"sizes",", and priority/preload treatment depending on the framework.",[10,559,244],{},[170,561,564],{"className":562,"code":563,"language":250,"meta":175},[248],"Identify the likely LCP element on this route before and after the change.\nCheck whether the PR changed image loading, dimensions, priority/preload, font loading, client rendering, or above-the-fold data fetching.\nIf there is LCP risk, propose a minimal fix.\n",[144,565,563],{"__ignoreMap":175},[26,567,569],{"id":568},"_4-watch-inp-effects-handlers-and-generated-interactivity","4. Watch INP: effects, handlers, and generated interactivity",[10,571,572,576],{},[17,573,575],{"href":574},"/blog/improve-inp-score/","Interaction to Next Paint"," gets worse when the main thread is busy after a user interacts.",[10,578,579],{},"AI-generated UI often adds more interactivity than the ticket really needed:",[100,581,582,585,588,591,594,597,603],{},[37,583,584],{},"search-as-you-type without debouncing",[37,586,587],{},"filters that recompute a large list on every keypress",[37,589,590],{},"animations triggered on every scroll event",[37,592,593],{},"charts that re-render on hover",[37,595,596],{},"global event listeners without cleanup",[37,598,599,602],{},[144,600,601],{},"useEffect"," chains that update state repeatedly",[37,604,605],{},"client-side sorting/filtering that should be server-side or precomputed",[10,607,608],{},"Look for code like this:",[170,610,612],{"className":276,"code":611,"language":278,"meta":175,"style":175},"\u003Cinput onChange={(event) => setQuery(event.target.value)} />\n\nconst filtered = items.filter(item =>\n  item.title.toLowerCase().includes(query.toLowerCase())\n)\n",[144,613,614,645,649,675,697],{"__ignoreMap":175},[179,615,616,618,622,625,627,630,633,636,639,642],{"class":181,"line":182},[179,617,502],{"class":301},[179,619,621],{"class":620},"s9eBZ","input",[179,623,624],{"class":324}," onChange",[179,626,359],{"class":297},[179,628,629],{"class":301},"{(",[179,631,632],{"class":331},"event",[179,634,635],{"class":301},") ",[179,637,638],{"class":297},"=>",[179,640,641],{"class":324}," setQuery",[179,643,644],{"class":301},"(event.target.value)} />\n",[179,646,647],{"class":181,"line":188},[179,648,292],{"emptyLinePlaceholder":291},[179,650,651,654,657,660,663,666,669,672],{"class":181,"line":194},[179,652,653],{"class":297},"const",[179,655,656],{"class":347}," filtered",[179,658,659],{"class":297}," =",[179,661,662],{"class":301}," items.",[179,664,665],{"class":324},"filter",[179,667,668],{"class":301},"(",[179,670,671],{"class":331},"item",[179,673,674],{"class":297}," =>\n",[179,676,677,680,683,686,689,692,694],{"class":181,"line":200},[179,678,679],{"class":301},"  item.title.",[179,681,682],{"class":324},"toLowerCase",[179,684,685],{"class":301},"().",[179,687,688],{"class":324},"includes",[179,690,691],{"class":301},"(query.",[179,693,682],{"class":324},[179,695,696],{"class":301},"())\n",[179,698,699],{"class":181,"line":315},[179,700,701],{"class":301},")\n",[10,703,704],{},"That is fine for 20 items. It is not fine for 5,000 items plus expensive rendering.",[10,706,707],{},"Ask:",[100,709,710,713,716,719,722,725],{},[37,711,712],{},"How many DOM nodes can this render?",[37,714,715],{},"Does it run on every keystroke?",[37,717,718],{},"Can it debounce or defer work?",[37,720,721],{},"Can it virtualize large lists?",[37,723,724],{},"Can it move expensive calculations outside render?",[37,726,727],{},"Does it need JavaScript at all?",[10,729,244],{},[170,731,734],{"className":732,"code":733,"language":250,"meta":175},[248],"Review this PR for INP risk.\nFocus on event handlers, effects, large renders, animations, scroll listeners, filters, search inputs, and client-side data processing.\nReturn the top 5 likely main-thread risks and how to measure each one.\n",[144,735,733],{"__ignoreMap":175},[26,737,739],{"id":738},"_5-do-not-let-generated-ui-create-cls","5. Do not let generated UI create CLS",[10,741,742,746],{},[17,743,745],{"href":744},"/blog/fix-cumulative-layout-shift/","Cumulative Layout Shift"," regressions are easy to miss in code review because the page often looks fine after it settles.",[10,748,749],{},"AI agents can create CLS by:",[100,751,752,763,766,769,772,775,778],{},[37,753,754,755,758,759,762],{},"removing ",[144,756,757],{},"width"," and ",[144,760,761],{},"height"," from images",[37,764,765],{},"adding late-loading banners above content",[37,767,768],{},"inserting ads without reserved slots",[37,770,771],{},"using skeletons that do not match final dimensions",[37,773,774],{},"changing font loading without fallbacks",[37,776,777],{},"rendering a different mobile layout after hydration",[37,779,780],{},"adding expandable sections above the main content",[10,782,783],{},"CLS review is mostly about reservation. If something loads late, it needs space before it arrives.",[10,785,244],{},[170,787,790],{"className":788,"code":789,"language":250,"meta":175},[248],"Review this UI change for CLS risk.\nCheck images, embeds, ads, banners, skeletons, fonts, mobile layout changes, and hydration-only content.\nList anything that appears after initial render and whether its space is reserved.\n",[144,791,789],{"__ignoreMap":175},[26,793,795],{"id":794},"_6-be-suspicious-of-small-third-party-scripts","6. Be suspicious of “small” third-party scripts",[10,797,798],{},"Agents often reach for third-party services because they solve the problem quickly:",[100,800,801,804,807,810,813,816,819,822],{},[37,802,803],{},"analytics snippets",[37,805,806],{},"chat widgets",[37,808,809],{},"calendars",[37,811,812],{},"maps",[37,814,815],{},"embeds",[37,817,818],{},"A/B testing tools",[37,820,821],{},"social pixels",[37,823,824],{},"video players",[10,826,827],{},"Each script can affect LCP, INP, or both. Some also shift layout.",[10,829,830],{},"If the agent adds a third party, require a short justification:",[100,832,833,836,839,842,845,848],{},[37,834,835],{},"What does it do?",[37,837,838],{},"Does it load on every route?",[37,840,841],{},"Can it be delayed until user intent?",[37,843,844],{},"Can it load after consent or interaction?",[37,846,847],{},"Can a server-side or static alternative work?",[37,849,850],{},"Is it isolated from critical rendering?",[10,852,853,854,858],{},"Use the ",[17,855,857],{"href":856},"/blog/third-party-scripts-core-web-vitals/","third-party scripts Core Web Vitals guide"," as the review companion.",[26,860,862],{"id":861},"_7-make-the-agent-produce-evidence-not-confidence","7. Make the agent produce evidence, not confidence",[10,864,865],{},"A good AI coding agent can also run checks. Use that.",[10,867,868],{},"For a frontend PR, ask for:",[100,870,871,874,877,880,883,886,889],{},[37,872,873],{},"production build output",[37,875,876],{},"route bundle diff",[37,878,879],{},"dependency diff",[37,881,882],{},"Lighthouse result before/after",[37,884,885],{},"screenshots of the changed route",[37,887,888],{},"list of changed client/server boundaries",[37,890,891],{},"suspected effects on LCP, INP, and CLS",[10,893,894],{},"Do not accept “this should improve performance” as evidence. Ask for numbers.",[10,896,897],{},"A solid agent report looks like this:",[170,899,902],{"className":900,"code":901,"language":250,"meta":175},[248],"Performance review:\n- New dependencies: none\n- First Load JS: /pricing increased from 112 kB to 118 kB (+6 kB)\n- LCP candidate: hero heading, unchanged\n- INP risk: pricing toggle now re-renders 18 cards, acceptable but should be tested on mobile\n- CLS risk: FAQ accordion reserves no extra space, but content expands only after user interaction\n- Lighthouse mobile: Performance 91 -> 90, no major regression\n- Recommendation: safe to merge, monitor INP in field data\n",[144,903,901],{"__ignoreMap":175},[10,905,906],{},"That is much more useful than a generic “tests passed”.",[26,908,910],{"id":909},"_8-add-performance-guardrails-to-your-agent-instructions","8. Add performance guardrails to your agent instructions",[10,912,913],{},"If you use Claude Code, Codex, or another repo-aware agent, put performance rules in the project instructions file.",[10,915,916],{},"Example:",[170,918,921],{"className":919,"code":920,"language":250,"meta":175},[248],"Frontend performance guardrails:\n- Do not add new runtime dependencies without explaining bundle impact.\n- Prefer server-rendered/static components unless interactivity is required.\n- Do not add \"use client\" to route/layout components without justification.\n- Preserve image width/height, sizes, priority/preload, and lazy-loading intent.\n- Keep the LCP element renderable without client-side JavaScript where possible.\n- Avoid scroll listeners, large re-renders, and unbounded client-side filtering.\n- Reserve space for images, ads, embeds, banners, and skeleton states.\n- Run the production build and report bundle/output changes for frontend PRs.\n- For performance-sensitive routes, include Lighthouse or WebPageTest before/after notes.\n- Never deploy automatically; open a PR with risks and verification results.\n",[144,922,920],{"__ignoreMap":175},[10,924,925],{},"This is boring. Boring is good. Agents are much safer when the rules are explicit.",[26,927,929],{"id":928},"_9-use-route-budgets-for-ai-generated-changes","9. Use route budgets for AI-generated changes",[10,931,932],{},"A simple route budget stops performance review from becoming subjective.",[10,934,935],{},"For example:",[100,937,938,941,944,947,950],{},[37,939,940],{},"marketing pages: no new client JavaScript unless necessary",[37,942,943],{},"blog pages: static HTML preferred, no interactive widgets by default",[37,945,946],{},"app dashboard pages: route chunk increase over 20 kB needs justification",[37,948,949],{},"checkout/contact flows: no third-party scripts added without approval",[37,951,952],{},"image-heavy pages: LCP image must keep dimensions and priority handling",[10,954,955],{},"You do not need a perfect budget. You need enough friction that an agent cannot accidentally add 90 kB for a cosmetic feature.",[26,957,959],{"id":958},"_10-the-ai-frontend-pr-checklist","10. The AI frontend PR checklist",[10,961,962],{},"Copy this into your PR template or agent review prompt.",[964,965,967],"h3",{"id":966},"dependencies","Dependencies",[100,969,972,980,986,992],{"className":970},[971],"contains-task-list",[37,973,976,979],{"className":974},[975],"task-list-item",[621,977],{"disabled":291,"type":978},"checkbox"," Did the PR add new runtime dependencies?",[37,981,983,985],{"className":982},[975],[621,984],{"disabled":291,"type":978}," Are they used on initial load or only lazy routes?",[37,987,989,991],{"className":988},[975],[621,990],{"disabled":291,"type":978}," Is there a smaller/native/CSS alternative?",[37,993,995,997],{"className":994},[975],[621,996],{"disabled":291,"type":978}," Did the production bundle size change?",[964,999,1001],{"id":1000},"rendering-boundaries","Rendering boundaries",[100,1003,1005,1011,1020],{"className":1004},[971],[37,1006,1008,1010],{"className":1007},[975],[621,1009],{"disabled":291,"type":978}," Did any server/static component become client-rendered?",[37,1012,1014,1016,1017,1019],{"className":1013},[975],[621,1015],{"disabled":291,"type":978}," Were new ",[144,1018,265],{},", hydrated islands, or global plugins added?",[37,1021,1023,1025],{"className":1022},[975],[621,1024],{"disabled":291,"type":978}," Can interactivity be pushed lower in the component tree?",[964,1027,1029],{"id":1028},"lcp","LCP",[100,1031,1033,1039,1048,1054],{"className":1032},[971],[37,1034,1036,1038],{"className":1035},[975],[621,1037],{"disabled":291,"type":978}," Is the LCP element still obvious?",[37,1040,1042,1044,1045,1047],{"className":1041},[975],[621,1043],{"disabled":291,"type":978}," Did hero image loading, priority, dimensions, or ",[144,1046,556],{}," change?",[37,1049,1051,1053],{"className":1050},[975],[621,1052],{"disabled":291,"type":978}," Did above-the-fold content move behind client fetch or loading states?",[37,1055,1057,1059],{"className":1056},[975],[621,1058],{"disabled":291,"type":978}," Did fonts or animations delay initial text/image rendering?",[964,1061,1063],{"id":1062},"inp","INP",[100,1065,1067,1073,1079,1085],{"className":1066},[971],[37,1068,1070,1072],{"className":1069},[975],[621,1071],{"disabled":291,"type":978}," Were expensive event handlers added?",[37,1074,1076,1078],{"className":1075},[975],[621,1077],{"disabled":291,"type":978}," Do filters/search/sorting run on every keystroke?",[37,1080,1082,1084],{"className":1081},[975],[621,1083],{"disabled":291,"type":978}," Are effects causing repeated state updates?",[37,1086,1088,1090],{"className":1087},[975],[621,1089],{"disabled":291,"type":978}," Are animations or scroll listeners doing main-thread work?",[964,1092,1094],{"id":1093},"cls","CLS",[100,1096,1098,1104,1110],{"className":1097},[971],[37,1099,1101,1103],{"className":1100},[975],[621,1102],{"disabled":291,"type":978}," Do images, ads, embeds, and skeletons reserve space?",[37,1105,1107,1109],{"className":1106},[975],[621,1108],{"disabled":291,"type":978}," Did banners, cookie prompts, or alerts appear above content?",[37,1111,1113,1115],{"className":1112},[975],[621,1114],{"disabled":291,"type":978}," Does the mobile layout match before and after hydration?",[964,1117,1119],{"id":1118},"third-parties","Third parties",[100,1121,1123,1129,1135],{"className":1122},[971],[37,1124,1126,1128],{"className":1125},[975],[621,1127],{"disabled":291,"type":978}," Were analytics, chat, maps, embeds, pixels, or tag scripts added?",[37,1130,1132,1134],{"className":1131},[975],[621,1133],{"disabled":291,"type":978}," Do they load only when needed?",[37,1136,1138,1140],{"className":1137},[975],[621,1139],{"disabled":291,"type":978}," Can they be delayed, consent-gated, or replaced?",[964,1142,1144],{"id":1143},"evidence","Evidence",[100,1146,1148,1154,1160,1166],{"className":1147},[971],[37,1149,1151,1153],{"className":1150},[975],[621,1152],{"disabled":291,"type":978}," Production build ran successfully.",[37,1155,1157,1159],{"className":1156},[975],[621,1158],{"disabled":291,"type":978}," Bundle/build output was compared.",[37,1161,1163,1165],{"className":1162},[975],[621,1164],{"disabled":291,"type":978}," Lighthouse or lab test was run for affected routes.",[37,1167,1169,1171],{"className":1168},[975],[621,1170],{"disabled":291,"type":978}," The agent explained performance risks in plain English.",[26,1173,1175],{"id":1174},"example-prompt-make-the-agent-review-its-own-work","Example prompt: make the agent review its own work",[10,1177,1178],{},"Use this after an agent finishes a frontend task:",[170,1180,1183],{"className":1181,"code":1182,"language":250,"meta":175},[248],"Before I review this PR, do a frontend performance self-review.\n\nCheck the diff for:\n- new dependencies\n- increased client-side JavaScript\n- changed server/client boundaries\n- LCP image or above-the-fold changes\n- INP risks from handlers/effects/renders\n- CLS risks from images, ads, embeds, skeletons, and late content\n- third-party scripts\n\nRun the production build. If the framework prints bundle or route-size output, summarize the relevant changes.\nDo not make more changes unless you find a clear regression. Return:\n1. likely LCP impact,\n2. likely INP impact,\n3. likely CLS impact,\n4. bundle/dependency impact,\n5. whether this is safe to merge.\n",[144,1184,1182],{"__ignoreMap":175},[26,1186,1188],{"id":1187},"example-prompt-ask-the-agent-to-fix-a-regression","Example prompt: ask the agent to fix a regression",[10,1190,1191],{},"If Lighthouse or field data already caught a regression:",[170,1193,1196],{"className":1194,"code":1195,"language":250,"meta":175},[248],"This PR appears to have worsened Core Web Vitals.\nInvestigate without guessing.\n\nRoute: \u003CURL>\nMetric affected: \u003CLCP / INP / CLS>\nBefore: \u003Cnumber>\nAfter: \u003Cnumber>\nRelevant changed files: \u003Cfiles>\n\nFind the most likely cause in the diff. Prioritize:\n- LCP image/rendering/data changes for LCP\n- event handlers/effects/main-thread work for INP\n- missing dimensions/late content/reserved space for CLS\n\nPropose the smallest safe fix, explain the tradeoff, then implement it.\nRun the build and summarize before/after evidence.\n",[144,1197,1195],{"__ignoreMap":175},[26,1199,1201],{"id":1200},"what-this-means-for-real-teams","What this means for real teams",[10,1203,1204],{},"AI coding agents are not bad for performance. Unreviewed AI coding agents are bad for performance.",[10,1206,1207],{},"The best workflow is not “never let agents touch frontend code”. That throws away too much productivity. The better workflow is:",[34,1209,1210,1213,1216,1219,1222],{},[37,1211,1212],{},"let the agent draft the change",[37,1214,1215],{},"force it to produce performance evidence",[37,1217,1218],{},"review the risky parts humans are good at spotting",[37,1220,1221],{},"measure before merging",[37,1223,1224],{},"keep deploy approval separate from code generation",[10,1226,1227],{},"That is the same pattern Google DeepMind and other AI teams are talking about at a larger scale: capable agents need boundaries, permissions, monitoring, and review. For frontend teams, the practical version is simpler. Keep your agent out of production deploys, make it explain bundle/runtime changes, and do not merge UI code until LCP, INP, and CLS risks are checked.",[10,1229,1230,1231,1235],{},"If you want a quick external check, run the changed route through ",[17,1232,1234],{"href":1233},"/","PageSpeedFix"," after deploying to a preview URL. Use the output to ask the agent for targeted fixes instead of generic performance advice.",[26,1237,1239],{"id":1238},"final-recommendation","Final recommendation",[10,1241,1242],{},"For every AI-generated frontend PR, require one extra section:",[170,1244,1247],{"className":1245,"code":1246,"language":250,"meta":175},[248],"Core Web Vitals risk:\n- LCP:\n- INP:\n- CLS:\n- Bundle impact:\n- Third-party impact:\n- Verification run:\n",[144,1248,1246],{"__ignoreMap":175},[10,1250,1251],{},"That small ritual catches the most common agent mistakes: too much JavaScript, unstable layout, slow interactions, and unmeasured “improvements”.",[10,1253,1254],{},"AI agents can make your frontend faster. But only if you treat performance as a requirement, not a nice-to-have after the PR is already merged.",[1256,1257,1258],"style",{},"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 pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":175,"searchDepth":188,"depth":188,"links":1260},[1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272,1281,1282,1283,1284],{"id":28,"depth":188,"text":29},{"id":91,"depth":188,"text":92},{"id":131,"depth":188,"text":132},{"id":255,"depth":188,"text":256},{"id":452,"depth":188,"text":453},{"id":568,"depth":188,"text":569},{"id":738,"depth":188,"text":739},{"id":794,"depth":188,"text":795},{"id":861,"depth":188,"text":862},{"id":909,"depth":188,"text":910},{"id":928,"depth":188,"text":929},{"id":958,"depth":188,"text":959,"children":1273},[1274,1275,1276,1277,1278,1279,1280],{"id":966,"depth":194,"text":967},{"id":1000,"depth":194,"text":1001},{"id":1028,"depth":194,"text":1029},{"id":1062,"depth":194,"text":1063},{"id":1093,"depth":194,"text":1094},{"id":1118,"depth":194,"text":1119},{"id":1143,"depth":194,"text":1144},{"id":1174,"depth":188,"text":1175},{"id":1187,"depth":188,"text":1188},{"id":1200,"depth":188,"text":1201},{"id":1238,"depth":188,"text":1239},"AI coding agents can ship hidden JavaScript, layout shifts, and slow interactions. Use this frontend review checklist before merging Claude Code, Codex, or Copilot agent PRs.","md",[1288,1291,1294,1297,1300],{"question":1289,"answer":1290},"Can AI coding agents make Core Web Vitals worse?","Yes. Agents can add client-side dependencies, render large components on the wrong route, lazy-load the LCP image, create layout shifts, or add heavy event handlers. The problem is usually not that the code is broken, but that it is functional code with an unnoticed performance cost.",{"question":1292,"answer":1293},"Should I let Claude Code or Codex fix performance issues automatically?","Use agents to draft fixes and explain tradeoffs, but keep a human review gate. Ask for before-and-after measurements, bundle diffs, Lighthouse results, and a short risk summary before merging.",{"question":1295,"answer":1296},"What should I check first in an AI-generated frontend PR?","Start with new dependencies, changed client/server boundaries, image loading, layout reservations, and expensive input handlers. Those five checks catch many LCP, CLS, and INP regressions.",{"question":1298,"answer":1299},"Do AI-generated React components usually hurt performance?","Not always. Simple generated components are often fine. The risk increases when the agent adds animation libraries, chart libraries, client components, global state, or broad data fetching without measuring the runtime cost.",{"question":1301,"answer":1302},"How do I prompt an AI agent to avoid performance regressions?","Give it explicit guardrails: avoid new dependencies unless justified, keep components server-rendered where possible, preserve image priority and dimensions, run the build, compare bundle output, and explain likely effects on LCP, INP, and CLS.","/og/ai-coding-agents-core-web-vitals.png","AI coding agents and Core Web Vitals frontend review checklist",null,{},"/blog/ai-coding-agents-core-web-vitals","2026-06-19",11,[1311,1314,1317,1320,1323,1326],{"title":1312,"url":1313},"Claude Code overview - Anthropic","https://docs.anthropic.com/en/docs/claude-code/overview",{"title":1315,"url":1316},"Claude Code common workflows - Anthropic","https://docs.anthropic.com/en/docs/claude-code/common-workflows",{"title":1318,"url":1319},"How we're securing internal systems against increasingly capable and imperfectly aligned AI - Google DeepMind","https://news.google.com/rss/articles/CBMib0FVX3lxTFBwbVhyR04tNjU3OFI5RHI4TWVqQjVxeUQ5OTVTSHkybWpPc3UwRkhWVnRfRkFSTUZEREhadFE0TWpkZnFYMGJFR1BsVGJhaXQwSkw3eDhPR1lYM1lRX2doTS1rajFzQXV3eXVBWS1IQQ?oc=5",{"title":1321,"url":1322},"Optimize Interaction to Next Paint - web.dev","https://web.dev/articles/optimize-inp",{"title":1324,"url":1325},"Largest Contentful Paint - web.dev","https://web.dev/articles/lcp",{"title":1327,"url":1328},"Cumulative Layout Shift - web.dev","https://web.dev/articles/cls",{"title":5,"description":1285},"blog/ai-coding-agents-core-web-vitals",[1332,1333,1334,1335,1336,1337],"ai","coding-agents","core-web-vitals","frontend-performance","code-review","javascript","tt8-TYZpfMlYEgBIVHWOFLSVNy4ETlRtxXeYbFfwSq8",1783436339752]