惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
W
WeLiveSecurity
O
OpenAI News
N
News and Events Feed by Topic
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Webroot Blog
Webroot Blog
Google Online Security Blog
Google Online Security Blog
云风的 BLOG
云风的 BLOG
N
News | PayPal Newsroom
H
Hacker News: Front Page
博客园_首页
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
The Last Watchdog
The Last Watchdog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Heimdal Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
S
Schneier on Security
宝玉的分享
宝玉的分享
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Y
Y Combinator Blog
Cyberwarzone
Cyberwarzone
Microsoft Security Blog
Microsoft Security Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
GbyAI
GbyAI
Cloudbric
Cloudbric
TaoSecurity Blog
TaoSecurity Blog
人人都是产品经理
人人都是产品经理
P
Palo Alto Networks Blog
M
MIT News - Artificial intelligence
G
GRAHAM CLULEY
C
Check Point Blog
Apple Machine Learning Research
Apple Machine Learning Research
Last Week in AI
Last Week in AI
T
Troy Hunt's Blog
L
Lohrmann on Cybersecurity
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Proofpoint News Feed
Blog — PlanetScale
Blog — PlanetScale
量子位
博客园 - 聂微东
S
Securelist
博客园 - 三生石上(FineUI控件)
F
Full Disclosure
G
Google Developers Blog
L
LINUX DO - 热门话题
P
Proofpoint News Feed
AI
AI
PCI Perspectives
PCI Perspectives

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Core Web Vitals in 2026: The Practical Fixes for INP, LCP, and CLS That Actually Work
AttractivePe · 2026-04-29 · via DEV Community

Core Web Vitals in 2026: The Practical Fixes for INP, LCP, and CLS That Actually Work

Your Lighthouse score says 95. Your users say your site feels slow. Here's why — and exactly how to fix each metric with code you can ship today.


Why Your Lighthouse Score Is Lying to You

There's a hard truth most teams learn the painful way: the number in your terminal is not the number Google cares about.

Lighthouse runs in a simulated environment — a powerful desktop with a fast connection. Google's ranking signal comes from CrUX (Chrome User Experience Report), which measures real users on real devices on real networks. It looks at the 75th percentile over a rolling 28-day window. Your Lighthouse run is a lab test. CrUX is the field result.

I've seen teams spend weeks polishing a 98 Lighthouse score while their actual INP — the metric that measures how responsive your site feels — sat firmly in the "poor" bucket for every mobile user. The gap between lab and field can be enormous.

In 2026, the three metrics that matter are:

  • INP (Interaction to Next Paint) — does the site respond quickly when tapped?
  • LCP (Largest Contentful Paint) — does the main content appear fast?
  • CLS (Cumulative Layout Shift) — does the page jump around while loading?

Let's fix each one with real, ship-ready code.


Fix 1: INP — The Architecture Problem Nobody Diagnoses

INP replaced FID in March 2024, and it's the metric most teams are failing. Roughly 43% of sites still don't meet the 200ms "good" threshold. Unlike FID, which only measured input delay, INP measures the full round trip: from click to visual paint. Every interaction on the page, not just the first one.

Here's the classic scenario: a user taps a filter on a product listing page. That tap triggers a setState that runs through a context provider, re-renders 40+ components, recomputes a sort, and then paints the selected state. On a mid-range Android over 4G, that round trip can hit 480ms. The user feels the lag. Chrome reports it. Your CrUX score tanks.

The Fix: Break the Task, Then Yield

The single highest-impact change is introducing yield points with scheduler.yield(). Split the interaction into "visual feedback first, everything else second":

async function handleFilterTap(filter) {
  // Paint the selected state immediately
  setSelectedFilter(filter);
  await yieldToMain();

  // Then do the expensive work
  const filtered = applyFilters(products, filter);
  await yieldToMain();

  setResults(filtered);
}

function yieldToMain() {
  if ('scheduler' in window && 'yield' in window.scheduler) {
    return window.scheduler.yield();
  }
  return new Promise(resolve => setTimeout(resolve, 0));
}

Enter fullscreen mode Exit fullscreen mode

The perceived difference is dramatic. The filter chip highlights instantly. The list updates a tick later. INP at the 75th percentile typically drops by 60-65% with this pattern alone — no changes to the filtering logic required.

Layer 2: Defer Heavy Renders with useDeferredValue

In React, mark the filtered results as a deferred value so the input stays responsive while the heavy list re-renders in the background:

function ProductList({ products, filter }) {
  const deferredProducts = React.useDeferredValue(products);

  const filtered = useMemo(
    () => applyFilters(deferredProducts, filter),
    [deferredProducts, filter]
  );

  return (
    <ul>
      {filtered.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </ul>
  );
}

Enter fullscreen mode Exit fullscreen mode

Layer 3: Offload Hidden Long Tasks

Open Chrome DevTools → Performance panel → record an interaction → look for any task over 50ms. The most common culprit? Third-party analytics scripts running synchronous operations on every click. Move them to a web worker:

// main.js
const analyticsWorker = new Worker('/analytics-worker.js');

function trackEvent(event) {
  analyticsWorker.postMessage({
    type: 'track',
    payload: { event: event.name, data: event.data }
  });
}

// analytics-worker.js
self.onmessage = function(e) {
  if (e.data.type === 'track') {
    // Heavy serialization and network calls happen here,
    // off the main thread
    const serialized = JSON.stringify(e.data.payload);
    fetch('/api/analytics', {
      method: 'POST',
      body: serialized
    });
  }
};

Enter fullscreen mode Exit fullscreen mode

The pattern: INP rewards event handlers that do almost nothing synchronously. Anything expensive — filtering, sorting, logging, computation — gets yielded, deferred, or offloaded.


Fix 2: LCP — It's Almost Always the Hero Image

Every team has the same story: "we optimized the images" and LCP was still 3.2 seconds. Because "the images" isn't the fix — the LCP element is the fix, and it's almost always one specific image above the fold.

Step 1: Identify the LCP Element

Don't guess. Measure it in production with the web-vitals library:

import { onLCP } from 'web-vitals';

onLCP(metric => {
  console.log('LCP element:', metric.entries.at(-1)?.element);
  console.log('LCP value:', metric.value);
  // Send to your analytics pipeline
  sendToAnalytics(metric);
});

Enter fullscreen mode Exit fullscreen mode

Step 2: Preload the LCP Resource

One line in your <head> can cut LCP by 500-700ms:

<link rel="preload" as="image" href="/hero-banner.avif" type="image/avif">

Enter fullscreen mode Exit fullscreen mode

Don't preload everything — just the LCP resource. Preload abuse creates its own problems.

Step 3: Set Fetch Priority

Browsers are conservative about image priority by default. Tell them this one matters:

<img
  src="/hero-banner.avif"
  fetchpriority="high"
  alt="Hero banner"
  width="1200"
  height="600"
/>

Enter fullscreen mode Exit fullscreen mode

Step 4: Modern Formats + Responsive Sizes

Use AVIF with WebP fallback. The file size difference between JPEG and AVIF at equivalent quality is routinely 40-60%. And serve the right size — a responsive srcset is not optional:

<picture>
  <source
    type="image/avif"
    srcset="/hero-400.avif 400w, /hero-800.avif 800w, /hero-1200.avif 1200w"
    sizes="100vw"
  />
  <source
    type="image/webp"
    srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
    sizes="100vw"
  />
  <img
    src="/hero-1200.jpg"
    fetchpriority="high"
    alt="Hero banner"
    width="1200"
    height="600"
  />
</picture>

Enter fullscreen mode Exit fullscreen mode

Step 5: Never Lazy-Load the LCP Image

This mistake is still on production sites in 2026. loading="lazy" on the hero image guarantees a LCP regression. The browser waits to start loading it until the page is scrolled — but it's visible immediately.

When LCP Is a Font, Not an Image

If the LCP element is text depending on a custom font, use font-display: swap and preload the font file:

@font-face {
  font-family: 'BrandDisplay';
  src: url('/fonts/brand-display.woff2') format('woff2');
  font-display: swap;
  font-weight: 700;
}

Enter fullscreen mode Exit fullscreen mode

<link rel="preload" href="/fonts/brand-display.woff2" as="font" type="font/woff2" crossorigin>

Enter fullscreen mode Exit fullscreen mode

Accept the brief flash of fallback type. Users barely notice. Your 75th-percentile LCP will.


Fix 3: CLS — Three Rules That Cover Everything

CLS is the cheapest metric to fix and the one that makes your site look most amateur when you don't. When buttons jump under thumbs and ads push content down, people lose trust — even if they can't articulate why.

Rule 1: Explicit Dimensions on Every Image, Video, and Iframe

Even if you're styling with CSS, HTML width and height attributes let the browser reserve space before the asset loads:

<!-- Good: browser reserves space immediately -->
<img src="/product.jpg" width="800" height="600" alt="Product photo" />

<!-- Bad: no space reserved, page jumps when image loads -->
<img src="/product.jpg" alt="Product photo" />

Enter fullscreen mode Exit fullscreen mode

If you prefer CSS, use aspect-ratio — but the HTML attributes are simpler and work everywhere.

Rule 2: Reserve Space for Late-Injected Content

Ad slots, cookie banners, personalization widgets — anything that arrives after first paint needs height reserved upfront:

.ad-slot {
  min-height: 250px; /* Reserve space even before the ad loads */
}

.cookie-banner-container {
  min-height: 64px;
}

Enter fullscreen mode Exit fullscreen mode

If the slot stays empty, leave it empty. The shift is worse than the blank space.

Rule 3: Match Font Metrics with size-adjust

font-display: swap prevents invisible text but can cause a layout shift if your fallback font has different metrics. The size-adjust descriptor on @font-face eliminates that shift:

@font-face {
  font-family: 'BrandDisplay';
  src: url('/fonts/brand-display.woff2') format('woff2');
  font-display: swap;
  font-weight: 700;
}

@font-face {
  font-family: 'BrandDisplay';
  src: local('Arial');
  font-weight: 700;
  font-display: swap;
  size-adjust: 105.2%; /* Tune this to match the web font metrics */
  ascent-override: 98%;
  descent-override: 22%;
  line-gap-override: 0%;
}

Enter fullscreen mode Exit fullscreen mode

This technique is massively underused. Most teams haven't heard of it, and it can bring CLS from 0.15 down to 0.02.


The Monitoring Setup That Makes This Stick

You can't fix what you can't see, and DevTools on your laptop is not "seeing." Here's the monitoring stack that actually helps:

1. Ship Real-User Metrics

import { onINP, onLCP, onCLS } from 'web-vitals';

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    delta: metric.delta,
    id: metric.id,
    navigationType: metric.navigationType,
    url: window.location.href,
    // Slice by route, device, country
    route: window.location.pathname,
    deviceType: /Mobi|Android/i.test(navigator.userAgent) ? 'mobile' : 'desktop',
  });

  if (navigator.sendBeacon) {
    navigator.sendBeacon('/api/vitals', body);
  } else {
    fetch('/api/vitals', { body, method: 'POST', keepalive: true });
  }
}

onINP(sendToAnalytics);
onLCP(sendToAnalytics);
onCLS(sendToAnalytics);

Enter fullscreen mode Exit fullscreen mode

2. Slice the Data

A single aggregate INP number hides everything. The same site can have great INP for desktop users in Germany and awful INP for mobile users in Brazil. The aggregate looks "mid." Slice by route, device class, and country to find the real fires.

3. Set Alerts at 80% of Thresholds

Don't wait until you're failing. Set alerts at:

  • INP at 160ms (80% of 200ms threshold)
  • LCP at 2.0s (80% of 2.5s threshold)
  • CLS at 0.08 (80% of 0.1 threshold)

A deploy that bumps INP from 150ms to 190ms still reports "good," but three of those in a month and you're failing.


FAQ: Common Gotchas

Q: I fixed INP on my local machine but CrUX still shows poor. Why?
A: CrUX is a 28-day rolling window. Your fix won't fully appear in the data for up to 4 weeks. Check your own real-user monitoring pipeline for immediate validation.

Q: scheduler.yield() isn't available in my target browsers.
A: The setTimeout(resolve, 0) fallback in the yieldToMain() function above handles this. It's slightly less optimal than native scheduler.yield() but still delivers the core benefit of breaking up long tasks.

Q: Do I need to preload every image on the page?
A: No — only the LCP element. Preloading too many resources creates contention and can actually hurt performance. One preload for one LCP resource.

Q: My CLS is caused by a third-party chat widget. How do I fix that?
A: Reserve space for it. Add min-height to the container where the widget renders. If the widget doesn't load, the empty space is better than the shift.

Q: Is it worth optimizing for Core Web Vitals if my site already ranks well?
A: Yes. Google has confirmed Core Web Vitals are a ranking signal. Beyond SEO, better vitals directly correlate with lower bounce rates and higher conversion rates. Performance is a product feature.


The Three Things I'd Tell You on Day One

  1. Stop optimizing for Lighthouse. Use it as a diagnostic, not a scoreboard. The scoreboard lives at CrUX, and the gap between the two can be an order of magnitude.

  2. Fix the metric that's actually failing. Don't work on LCP because you find it satisfying while INP silently tanks. Look at your CrUX data, find the worst metric, and start there.

  3. Treat performance as a product feature. The teams that succeed at Core Web Vitals are the ones where someone owns the metrics, they're reviewed weekly, regressions are treated as bugs, and they're part of the definition of done. The teams that fail are the ones that treat vitals as "something to get to after the next launch."

Your users don't care about your Lighthouse score. They care about whether the button responds when they tap it. Fix that.


If this helped, follow for more on frontend performance, architecture, and the unglamorous parts of shipping software.