DEV Community

Beksultan Sagnaev
Beksultan Sagnaev

Posted on

How We Turned a High-Traffic News Site's Core Web Vitals from Red to Green

The setup: a news site where every millisecond is public

Kapital.kz is one of Kazakhstan's high-traffic business and finance news portals, built on Next.js. Unlike a typical marketing site, a news portal has a brutal constraint: every page is a landing page. Every article is someone's entry point from Google, and Google's Core Web Vitals directly influence how well those articles rank. A slow, janky article page doesn't just annoy readers — it actively pushes the site down in search results.

When I joined the team at Musan Group, all three Core Web Vitals metrics — LCP, INP, and CLS — were sitting in the red zone on finance.kapital.kz. For context, Google's thresholds put "poor" LCP above 4.0s (versus "good" at 2.5s or under), "poor" INP above 500ms (versus 200ms or under), and "poor" CLS above 0.25 (versus 0.1 or under) — that's the red-to-green gap we were closing. Here's what that actually meant in practice, and what it took to fix each one.

LCP: the hero image problem

Largest Contentful Paint measures how long it takes for the biggest visible element — usually the hero image or headline — to render. On a news site, that's almost always the article's cover image.
The fixes here were less about clever tricks and more about removing waste: cutting unnecessary client-side work that delayed the image request, and making sure the image itself wasn't fighting other resources for bandwidth on first load. Combined with general rendering path cleanup, this brought LCP on finance.kapital.kz down out of the red zone.

INP: the interaction that made the page feel broken

Interaction to Next Paint replaced FID as a Core Web Vital, and it measures something more honest: not just "did the first click register fast," but "does every interaction on this page feel instant, for the whole time the user is on it." A page can have great LCP and still feel broken if scrolling stutters or a click takes half a second to respond.

Two things were quietly wrecking INP on article pages:
Google Ads scripts loaded incorrectly. Ad scripts are notorious for blocking the main thread — if they're not loaded with the right strategy (deferred, lazy, or isolated from critical rendering work), every ad slot on the page becomes a tax on every single interaction, not just page load.

Heavy libraries sitting in the main bundle. Some dependencies had no business being loaded upfront on every article page. Moving them out of the initial JS bundle — loading them only when actually needed — freed up the main thread to actually respond to user input instead of parsing and executing code nobody was using yet.

Between fixing the ad loading strategy and pulling heavy libraries out of the critical path, INP moved into the green zone.

CLS: the classic culprits — ads and infinite scroll

Cumulative Layout Shift measures visual stability — how much content jumps around as the page loads. For a news site, the two biggest offenders are almost always the same:

Ads without reserved space. When an ad slot doesn't have a fixed size reserved before the ad itself loads, the page content jumps down the moment the ad finally renders — classic CLS violation, and one of the most common on ad-supported sites specifically.

Infinite scroll done carelessly. As new articles load in below the fold, any miscalculation in how their height is estimated before the images finish loading causes the whole feed to jitter and shift under the reader's thumb.

Fixing both — properly reserving space for ad units before they load, and correcting how infinite-scroll batches estimate their height before content is fully rendered — eliminated the layout shift almost entirely.

Finding the problems in the first place: BigQuery over guesswork

Here's the part that made the difference between guessing at fixes and actually targeting the right ones: instead of relying only on lab data (Lighthouse scores, which are a single synthetic run), the team pulled real user monitoring data through BigQuery — actual field data from actual readers, on actual devices and connections across Kazakhstan.

This matters because lab data lies by omission. A Lighthouse run on a fast office connection tells you nothing about what a reader on a mid-range Android phone over a spotty mobile connection in a smaller city actually experiences. BigQuery let us see the real distribution of LCP/INP/CLS across the actual user base, and — critically — segment it: which specific pages, which devices, which network conditions were dragging the aggregate numbers down. That turned "the site feels slow" into a ranked list of specific, fixable bottlenecks.

The other half of technical SEO: making Google understand the content, not just render it fast

Core Web Vitals is only one pillar. The other half of technical SEO for a news site is making sure search engines can correctly parse what an article actually is — and this is where structured data and AMP come in.

Updating schema.org JSON-LD for news articles matters more than it sounds like it should. Structured data is how Google knows "this is a NewsArticle, published on this date, by this author, about this topic" instead of just guessing from raw HTML. Getting the JSON-LD schema right and current directly affects whether an article is eligible for rich results in search — the difference between a plain blue link and one with a headline, image, and publish date attached in the results page.

Fixing broken AMP versions closed a gap that's easy to overlook: if a site publishes both a regular and an AMP version of each article, and the AMP version is broken or out of sync, search engines either serve a degraded experience or lose trust in the AMP feed altogether — quietly hurting visibility for the very pages meant to load fastest on mobile search.

Alongside this, cleaning up HTML validation issues and fixing production bugs in the news card UI closed out the more mundane but still SEO-relevant half of the work: broken markup and inconsistent card rendering both chip away at how reliably a page renders — and gets crawled — across different devices and bots.

The takeaway

None of these fixes were exotic. Reserve space for ads. Defer what doesn't need to run immediately. Get your structured data right. Use field data, not just lab scores, to know what to fix first. What made it work wasn't a single trick — it was treating Core Web Vitals as a measurement problem first (find the real bottleneck via BigQuery) and a discipline problem second (systematically closing each gap: rendering path, ad loading, layout stability, structured data, AMP).

For a site where every article is a search-engine entry point, that discipline is the difference between ranking and not.


I work as a full-stack developer based in Astana, Kazakhstan, focused on Next.js, technical SEO, and performance optimization for high-traffic products. If you're dealing with Core Web Vitals or technical SEO issues on your own site, I'd be glad to compare notes — reach me on Telegram or LinkedIn, or check out more of my work at beksnotfound.kz.

Top comments (0)