<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AMAL</title>
    <description>The latest articles on DEV Community by AMAL (@amalmktk).</description>
    <link>https://dev.to/amalmktk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4113594%2F7ea46e00-e819-4aac-a56e-6cd148c33752.webp</url>
      <title>DEV Community: AMAL</title>
      <link>https://dev.to/amalmktk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amalmktk"/>
    <language>en</language>
    <item>
      <title>Web Vitals in JavaScript: How to Measure, Debug, and Fix Real-World Performance Problems</title>
      <dc:creator>AMAL</dc:creator>
      <pubDate>Tue, 08 Sep 2026 02:50:03 +0000</pubDate>
      <link>https://dev.to/amalmktk/web-vitals-in-javascript-how-to-measure-debug-and-fix-real-world-performance-problems-1l2d</link>
      <guid>https://dev.to/amalmktk/web-vitals-in-javascript-how-to-measure-debug-and-fix-real-world-performance-problems-1l2d</guid>
      <description>&lt;p&gt;A website can work perfectly and still feel slow.&lt;/p&gt;

&lt;p&gt;The API responds.&lt;br&gt;
The page loads.&lt;br&gt;
There are no JavaScript errors.&lt;/p&gt;

&lt;p&gt;But users might still experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content taking too long to appear&lt;/li&gt;
&lt;li&gt;Buttons feeling unresponsive&lt;/li&gt;
&lt;li&gt;Layout jumping while the page loads&lt;/li&gt;
&lt;li&gt;Images appearing late&lt;/li&gt;
&lt;li&gt;UI freezing during interactions&lt;/li&gt;
&lt;li&gt;Content moving unexpectedly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where &lt;strong&gt;Web Vitals&lt;/strong&gt; become useful.&lt;/p&gt;

&lt;p&gt;Web Vitals give us measurable signals for understanding how users actually experience a website.&lt;/p&gt;

&lt;p&gt;The three &lt;strong&gt;Core Web Vitals&lt;/strong&gt; are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LCP&lt;/strong&gt; — Largest Contentful Paint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INP&lt;/strong&gt; — Interaction to Next Paint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLS&lt;/strong&gt; — Cumulative Layout Shift&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we'll look at what these metrics mean, how JavaScript affects them, how to measure them, how to debug problems, and most importantly, how to fix them.&lt;/p&gt;


&lt;h2&gt;
  
  
  What are Web Vitals?
&lt;/h2&gt;

&lt;p&gt;Web Vitals are a set of metrics designed to measure important aspects of the user experience on the web.&lt;/p&gt;

&lt;p&gt;The three Core Web Vitals focus on:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Loading performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;INP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Responsiveness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CLS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Visual stability&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are other useful metrics such as &lt;strong&gt;TTFB&lt;/strong&gt; and &lt;strong&gt;FCP&lt;/strong&gt;, but LCP, INP, and CLS are the three Core Web Vitals.&lt;/p&gt;

&lt;p&gt;The important thing to understand is that these aren't simply JavaScript metrics.&lt;/p&gt;

&lt;p&gt;They measure the &lt;strong&gt;user's experience&lt;/strong&gt;, and JavaScript is one of many factors that can influence that experience.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Slow server&lt;br&gt;
↓&lt;br&gt;
Slow HTML response&lt;br&gt;
↓&lt;br&gt;
Resources start loading late&lt;br&gt;
↓&lt;br&gt;
Content renders late&lt;br&gt;
↓&lt;br&gt;
Poor LCP&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;p&gt;Large JavaScript task&lt;br&gt;
↓&lt;br&gt;
Main thread blocked&lt;br&gt;
↓&lt;br&gt;
User clicks a button&lt;br&gt;
↓&lt;br&gt;
Browser can't respond immediately&lt;br&gt;
↓&lt;br&gt;
Poor INP&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;p&gt;Image has no dimensions&lt;br&gt;
↓&lt;br&gt;
Page renders&lt;br&gt;
↓&lt;br&gt;
Image loads&lt;br&gt;
↓&lt;br&gt;
Content moves&lt;br&gt;
↓&lt;br&gt;
Poor CLS&lt;/p&gt;

&lt;p&gt;Understanding these chains is much more useful than simply looking at a score.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. LCP — Largest Contentful Paint
&lt;/h2&gt;

&lt;p&gt;LCP measures how quickly the largest content element visible in the viewport is rendered.&lt;/p&gt;

&lt;p&gt;This might be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A hero image&lt;/li&gt;
&lt;li&gt;A large heading&lt;/li&gt;
&lt;li&gt;A product image&lt;/li&gt;
&lt;li&gt;A banner&lt;/li&gt;
&lt;li&gt;A large block of text&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In simple terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How long does it take before the main content of the page becomes visible?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  LCP thresholds
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;LCP&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;≤ 2.5s&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.5s – 4.0s&lt;/td&gt;
&lt;td&gt;Needs improvement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt; 4.0s&lt;/td&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The goal is to keep LCP at 2.5 seconds or less for most users.&lt;/p&gt;
&lt;h3&gt;
  
  
  What causes a bad LCP?
&lt;/h3&gt;

&lt;p&gt;A common assumption is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"My LCP is bad because my image is too large."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That can be true, but LCP can be affected by several different stages.&lt;/p&gt;

&lt;p&gt;Think of LCP as:&lt;br&gt;
TTFB&lt;br&gt;
+&lt;br&gt;
Resource Load Delay&lt;br&gt;
+&lt;br&gt;
Resource Load Time&lt;br&gt;
+&lt;br&gt;
Render Delay&lt;/p&gt;

&lt;p&gt;LCP&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
Server response 1.2s&lt;br&gt;
Resource load delay 0.6s&lt;br&gt;
Image download 1.5s&lt;br&gt;
Render delay 0.7s&lt;/p&gt;

&lt;p&gt;LCP 4.0s&lt;/p&gt;

&lt;p&gt;Optimizing only the image wouldn't necessarily solve the entire problem.&lt;/p&gt;
&lt;h3&gt;
  
  
  Finding the LCP element
&lt;/h3&gt;

&lt;p&gt;Chrome DevTools is one of the best places to start.&lt;/p&gt;

&lt;p&gt;Open:&lt;/p&gt;

&lt;p&gt;DevTools&lt;br&gt;
→ Performance&lt;br&gt;
→ Record&lt;br&gt;
→ Reload the page&lt;/p&gt;

&lt;p&gt;Look for the LCP marker in the Performance timeline.&lt;/p&gt;

&lt;p&gt;You can also inspect LCP programmatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PerformanceObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntries&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LCP:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Element:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;largest-contentful-paint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;buffered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which element is actually responsible for my LCP?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For production applications, the &lt;code&gt;web-vitals&lt;/code&gt; library is usually a better option because it handles the metric reporting details for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common LCP problems
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Slow server response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your server takes too long to return HTML, everything downstream is delayed.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Request&lt;br&gt;
↓&lt;br&gt;
Server processing&lt;br&gt;
↓&lt;br&gt;
Database query&lt;br&gt;
↓&lt;br&gt;
API calls&lt;br&gt;
↓&lt;br&gt;
HTML response&lt;/p&gt;

&lt;p&gt;If this takes 1.5 seconds, you've already consumed a significant portion of your LCP budget.&lt;/p&gt;

&lt;p&gt;What can you do?&lt;/p&gt;

&lt;p&gt;Look at TTFB — Time to First Byte.&lt;/p&gt;

&lt;p&gt;Potential solutions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server-side caching&lt;/li&gt;
&lt;li&gt;CDN caching&lt;/li&gt;
&lt;li&gt;Database optimization&lt;/li&gt;
&lt;li&gt;Reducing backend work&lt;/li&gt;
&lt;li&gt;Static generation&lt;/li&gt;
&lt;li&gt;Streaming HTML&lt;/li&gt;
&lt;li&gt;Edge rendering&lt;/li&gt;
&lt;li&gt;Reducing unnecessary API calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Large images&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Images are common LCP elements.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/hero.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/hero.webp"&lt;/span&gt;
  &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"1200"&lt;/span&gt;
  &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"600"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Product dashboard"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use an appropriate image format and size.&lt;/p&gt;

&lt;p&gt;Don't send a 4000px image to a device displaying it at 600px.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Lazy-loading the LCP image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a common mistake.&lt;/p&gt;

&lt;p&gt;If your hero image is the LCP element, don't automatically add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/hero.webp"&lt;/span&gt;
  &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Hero"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lazy loading can delay a resource that is actually critical to the initial viewport.&lt;/p&gt;

&lt;p&gt;Lazy loading is generally more appropriate for images that are below the fold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. JavaScript delaying rendering&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;renderProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the page cannot render meaningful content until JavaScript finishes downloading, parsing, executing, and fetching data, LCP can suffer.&lt;/p&gt;

&lt;p&gt;Ask yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this JavaScript actually need to execute before the main content can appear?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If not, move it out of the critical path.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. INP — Interaction to Next Paint
&lt;/h2&gt;

&lt;p&gt;INP is about responsiveness.&lt;/p&gt;

&lt;p&gt;Imagine a user clicks:&lt;/p&gt;

&lt;p&gt;Add to cart&lt;/p&gt;

&lt;p&gt;The user expects the interface to respond quickly.&lt;/p&gt;

&lt;p&gt;If the browser is busy doing expensive JavaScript work, the interaction can feel frozen.&lt;/p&gt;

&lt;p&gt;INP measures interaction latency throughout the page lifecycle and focuses on how quickly the page can visually respond after user interactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  INP thresholds
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;INP&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;≤ 200ms&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200ms – 500ms&lt;/td&gt;
&lt;td&gt;Needs improvement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt; 500ms&lt;/td&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why JavaScript has a huge impact on INP
&lt;/h3&gt;

&lt;p&gt;JavaScript runs primarily on the browser's main thread.&lt;/p&gt;

&lt;p&gt;The main thread is responsible for many important browser tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;DOM&lt;/li&gt;
&lt;li&gt;Style calculation&lt;/li&gt;
&lt;li&gt;Layout&lt;/li&gt;
&lt;li&gt;Paint&lt;/li&gt;
&lt;li&gt;User input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you execute a long JavaScript task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processHugeDataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;_000_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// expensive work&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the browser may not be able to respond to user input until that work finishes.&lt;/p&gt;

&lt;p&gt;This creates a long task.&lt;/p&gt;

&lt;h3&gt;
  
  
  A real-world example
&lt;/h3&gt;

&lt;p&gt;Imagine a search page.&lt;/p&gt;

&lt;p&gt;The user types:&lt;/p&gt;

&lt;p&gt;laptop&lt;/p&gt;

&lt;p&gt;And your application does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hugeDataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;hugeDataset&lt;/code&gt; contains hundreds of thousands of records, every keystroke could trigger expensive work.&lt;/p&gt;

&lt;p&gt;The experience becomes:&lt;/p&gt;

&lt;p&gt;Keypress&lt;br&gt;
↓&lt;br&gt;
JavaScript runs&lt;br&gt;
↓&lt;br&gt;
Browser is busy&lt;br&gt;
↓&lt;br&gt;
Rendering is delayed&lt;br&gt;
↓&lt;br&gt;
Poor INP&lt;/p&gt;
&lt;h3&gt;
  
  
  How to improve INP
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Break up long tasks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of doing everything synchronously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;processEverything&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;break expensive work into smaller chunks.&lt;/p&gt;

&lt;p&gt;Depending on the workload, techniques include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;scheduler.yield()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setTimeout&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;requestIdleCallback&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Web Workers&lt;/li&gt;
&lt;li&gt;Chunked processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to give the browser opportunities to process other work between chunks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Move expensive work to a Web Worker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For CPU-heavy operations, Web Workers can keep expensive computation away from the main UI thread.&lt;/p&gt;

&lt;p&gt;Main Thread&lt;br&gt;
│&lt;br&gt;
│ message&lt;br&gt;
▼&lt;br&gt;
Web Worker&lt;br&gt;
│&lt;br&gt;
│ expensive calculation&lt;br&gt;
▼&lt;br&gt;
Main Thread&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Avoid unnecessary rendering&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In React applications, expensive rendering can become an interaction problem.&lt;/p&gt;

&lt;p&gt;Imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Search&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VeryLargeList&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ComplexDashboard&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Charts&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Analytics&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If changing the search input causes the entire application tree to render again, interactions can become expensive.&lt;/p&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unnecessary re-renders&lt;/li&gt;
&lt;li&gt;Expensive calculations&lt;/li&gt;
&lt;li&gt;Large component trees&lt;/li&gt;
&lt;li&gt;Huge lists&lt;/li&gt;
&lt;li&gt;Expensive effects&lt;/li&gt;
&lt;li&gt;Synchronous state updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Virtualization can also help when rendering large lists.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. CLS — Cumulative Layout Shift
&lt;/h2&gt;

&lt;p&gt;CLS measures visual stability.&lt;/p&gt;

&lt;p&gt;You've probably experienced this:&lt;/p&gt;

&lt;p&gt;You try to click:&lt;/p&gt;

&lt;p&gt;Buy Now&lt;/p&gt;

&lt;p&gt;But an advertisement loads above it.&lt;/p&gt;

&lt;p&gt;Suddenly:&lt;/p&gt;

&lt;p&gt;Buy Now&lt;/p&gt;

&lt;p&gt;moves down.&lt;/p&gt;

&lt;p&gt;You click.&lt;/p&gt;

&lt;p&gt;You accidentally click something else.&lt;/p&gt;

&lt;p&gt;That's a layout shift.&lt;/p&gt;

&lt;h3&gt;
  
  
  CLS thresholds
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CLS&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;≤ 0.1&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.1 – 0.25&lt;/td&gt;
&lt;td&gt;Needs improvement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt; 0.25&lt;/td&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Lower is better.&lt;/p&gt;

&lt;h3&gt;
  
  
  What causes CLS?
&lt;/h3&gt;

&lt;p&gt;Common causes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Images without dimensions&lt;/li&gt;
&lt;li&gt;Ads without reserved space&lt;/li&gt;
&lt;li&gt;Dynamically injected content&lt;/li&gt;
&lt;li&gt;Web fonts causing layout changes&lt;/li&gt;
&lt;li&gt;Components appearing after JavaScript executes&lt;/li&gt;
&lt;li&gt;Content inserted above existing content&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The classic image problem
&lt;/h3&gt;

&lt;p&gt;Bad:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/product.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Product"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/product.jpg"&lt;/span&gt;
  &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"800"&lt;/span&gt;
  &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"600"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Product"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.product-image&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;aspect-ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser can now reserve the required space before the image loads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic content can cause CLS
&lt;/h3&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;banner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;div class="promotion"&amp;gt;
    20% off today!
  &amp;lt;/div&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this happens after the page has rendered, everything below it can move.&lt;/p&gt;

&lt;p&gt;Instead, reserve the space from the beginning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.promotion-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fonts can cause layout shifts
&lt;/h3&gt;

&lt;p&gt;A custom font may load after the initial render.&lt;/p&gt;

&lt;p&gt;The browser initially renders:&lt;/p&gt;

&lt;p&gt;Fallback font&lt;/p&gt;

&lt;p&gt;Then switches to:&lt;/p&gt;

&lt;p&gt;Custom font&lt;/p&gt;

&lt;p&gt;If the font has different metrics, text dimensions can change and cause layout shifts.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;font-display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;swap&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use font metric overrides where appropriate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring Web Vitals with JavaScript
&lt;/h2&gt;

&lt;p&gt;One of the easiest ways to collect Web Vitals is the &lt;code&gt;web-vitals&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;Install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;web-vitals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onCLS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;web-vitals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;onCLS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful during development, but production monitoring is where things get much more interesting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending Web Vitals to your backend
&lt;/h3&gt;

&lt;p&gt;Instead of logging metrics to the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can send them to your analytics endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onCLS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;web-vitals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendToAnalytics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/web-vitals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;keepalive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sendToAnalytics&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sendToAnalytics&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;onCLS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sendToAnalytics&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your backend can collect real-user performance data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't just collect the number
&lt;/h3&gt;

&lt;p&gt;Knowing:&lt;/p&gt;

&lt;p&gt;LCP = 4.2 seconds&lt;/p&gt;

&lt;p&gt;isn't enough.&lt;/p&gt;

&lt;p&gt;You need to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which page?&lt;/li&gt;
&lt;li&gt;Which element?&lt;/li&gt;
&lt;li&gt;Which device?&lt;/li&gt;
&lt;li&gt;Which browser?&lt;/li&gt;
&lt;li&gt;Which connection?&lt;/li&gt;
&lt;li&gt;Which release?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metric"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LCP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/products"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"device"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mobile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chrome"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4g"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have something actionable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attribution: Finding the actual problem
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;web-vitals&lt;/code&gt; package also provides an attribution build.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onCLS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;web-vitals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onCLS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;web-vitals/attribution&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides additional diagnostic information that can help identify the cause of poor metric values.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the metric, attribution information can help answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which element was the LCP element?&lt;/li&gt;
&lt;li&gt;How long did the resource take?&lt;/li&gt;
&lt;li&gt;Was there render delay?&lt;/li&gt;
&lt;li&gt;Which DOM element was involved?&lt;/li&gt;
&lt;li&gt;Which interaction contributed to poor INP?&lt;/li&gt;
&lt;li&gt;Which elements shifted?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is much more useful than simply knowing that a metric is "bad."&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging LCP in practice
&lt;/h2&gt;

&lt;p&gt;Suppose production reports:&lt;/p&gt;

&lt;p&gt;LCP = 4.8s&lt;/p&gt;

&lt;p&gt;Don't immediately start optimizing random things.&lt;/p&gt;

&lt;p&gt;Start with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify the LCP element&lt;/li&gt;
&lt;li&gt;Determine when it was requested&lt;/li&gt;
&lt;li&gt;Determine when it finished loading&lt;/li&gt;
&lt;li&gt;Determine when it was rendered&lt;/li&gt;
&lt;li&gt;Find the largest delay&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example:&lt;br&gt;
TTFB 1.2s&lt;br&gt;
Resource load delay 0.8s&lt;br&gt;
Resource load time 1.9s&lt;br&gt;
Render delay 0.9s&lt;/p&gt;

&lt;p&gt;LCP 4.8s&lt;/p&gt;

&lt;p&gt;Now the problem is much clearer.&lt;/p&gt;

&lt;p&gt;You wouldn't solve this only by compressing the image.&lt;/p&gt;

&lt;p&gt;You'd investigate all four stages.&lt;/p&gt;
&lt;h2&gt;
  
  
  Debugging INP in practice
&lt;/h2&gt;

&lt;p&gt;Suppose:&lt;/p&gt;

&lt;p&gt;INP = 650ms&lt;/p&gt;

&lt;p&gt;The next question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which interaction is causing it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe it's:&lt;/p&gt;

&lt;p&gt;Click filter&lt;br&gt;
↓&lt;br&gt;
setState()&lt;br&gt;
↓&lt;br&gt;
Render 5,000 rows&lt;br&gt;
↓&lt;br&gt;
Sort data&lt;br&gt;
↓&lt;br&gt;
Calculate charts&lt;br&gt;
↓&lt;br&gt;
Browser finally paints&lt;/p&gt;

&lt;p&gt;This is where Chrome DevTools becomes extremely useful.&lt;/p&gt;

&lt;p&gt;Open:&lt;/p&gt;

&lt;p&gt;DevTools&lt;br&gt;
→ Performance&lt;br&gt;
→ Start recording&lt;br&gt;
→ Interact with the page&lt;br&gt;
→ Stop recording&lt;/p&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long tasks&lt;/li&gt;
&lt;li&gt;Long event handlers&lt;/li&gt;
&lt;li&gt;Large rendering operations&lt;/li&gt;
&lt;li&gt;Forced synchronous layout&lt;/li&gt;
&lt;li&gt;Excessive JavaScript execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't optimize blindly.&lt;/p&gt;

&lt;p&gt;Find the expensive interaction first.&lt;/p&gt;
&lt;h2&gt;
  
  
  Debugging CLS in practice
&lt;/h2&gt;

&lt;p&gt;If you see:&lt;/p&gt;

&lt;p&gt;CLS = 0.31&lt;/p&gt;

&lt;p&gt;you need to find the elements that moved.&lt;/p&gt;

&lt;p&gt;Chrome DevTools' Performance panel can help identify layout shifts.&lt;/p&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;p&gt;Image loaded&lt;br&gt;
↓&lt;br&gt;
Layout shift&lt;/p&gt;

&lt;p&gt;Ad inserted&lt;br&gt;
↓&lt;br&gt;
Layout shift&lt;/p&gt;

&lt;p&gt;Font loaded&lt;br&gt;
↓&lt;br&gt;
Layout shift&lt;/p&gt;

&lt;p&gt;React component mounted&lt;br&gt;
↓&lt;br&gt;
Layout shift&lt;/p&gt;

&lt;p&gt;Then reserve the appropriate space or change the rendering strategy.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lab data vs Real User Monitoring
&lt;/h2&gt;

&lt;p&gt;This distinction is extremely important.&lt;/p&gt;

&lt;p&gt;You might run Lighthouse and get:&lt;/p&gt;

&lt;p&gt;LCP: 1.8s&lt;br&gt;
INP: 80ms&lt;br&gt;
CLS: 0.02&lt;/p&gt;

&lt;p&gt;Then production users might experience:&lt;/p&gt;

&lt;p&gt;LCP: 4.1s&lt;br&gt;
INP: 320ms&lt;br&gt;
CLS: 0.18&lt;/p&gt;

&lt;p&gt;Both can be correct.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Your development machine might have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast CPU&lt;/li&gt;
&lt;li&gt;Fast network&lt;/li&gt;
&lt;li&gt;Large cache&lt;/li&gt;
&lt;li&gt;Desktop Chrome&lt;/li&gt;
&lt;li&gt;Low latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your users might have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Low-end Android&lt;/li&gt;
&lt;li&gt;Slow 4G&lt;/li&gt;
&lt;li&gt;Cold cache&lt;/li&gt;
&lt;li&gt;Older browser&lt;/li&gt;
&lt;li&gt;High latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's why Real User Monitoring (RUM) matters.&lt;/p&gt;

&lt;p&gt;Lab tools are excellent for controlled debugging.&lt;/p&gt;

&lt;p&gt;Real-user data tells you what is actually happening in production.&lt;/p&gt;
&lt;h3&gt;
  
  
  Don't optimize only for the average
&lt;/h3&gt;

&lt;p&gt;Suppose you have:&lt;/p&gt;

&lt;p&gt;Average LCP = 1.9s&lt;/p&gt;

&lt;p&gt;That sounds great.&lt;/p&gt;

&lt;p&gt;But perhaps:&lt;/p&gt;

&lt;p&gt;p75 LCP = 2.7s&lt;br&gt;
p95 LCP = 5.1s&lt;/p&gt;

&lt;p&gt;Averages can hide users who are having a poor experience.&lt;/p&gt;

&lt;p&gt;That's why performance analysis should consider percentiles, especially the 75th percentile.&lt;/p&gt;
&lt;h3&gt;
  
  
  Segment your Web Vitals
&lt;/h3&gt;

&lt;p&gt;When collecting production data, don't store only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2800&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include useful dimensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metric"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LCP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/products"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"device"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mobile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chrome"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4g"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"release"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.4.2"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can discover patterns.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Desktop LCP: 1.8s&lt;br&gt;
Mobile LCP: 3.9s&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;p&gt;Home page LCP: 1.7s&lt;br&gt;
Product page LCP: 4.2s&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;p&gt;Release 1.4.1: 2.0s&lt;br&gt;
Release 1.4.2: 3.1s&lt;/p&gt;

&lt;p&gt;That last one can reveal a performance regression introduced by a deployment.&lt;/p&gt;
&lt;h2&gt;
  
  
  A practical performance debugging workflow
&lt;/h2&gt;

&lt;p&gt;Here's the workflow I recommend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Measure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LCP&lt;/li&gt;
&lt;li&gt;INP&lt;/li&gt;
&lt;li&gt;CLS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't guess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Identify the bad metric&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;LCP → 4.3s ❌&lt;br&gt;
INP → 120ms ✅&lt;br&gt;
CLS → 0.04 ✅&lt;/p&gt;

&lt;p&gt;Now focus on LCP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 — Find the responsible element&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is the LCP element?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hero-image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have something concrete to investigate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — Break the metric into stages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For LCP:&lt;/p&gt;

&lt;p&gt;TTFB&lt;br&gt;
+&lt;br&gt;
Resource Load Delay&lt;br&gt;
+&lt;br&gt;
Resource Load Time&lt;br&gt;
+&lt;br&gt;
Render Delay&lt;/p&gt;

&lt;p&gt;Find the largest contributor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5 — Profile JavaScript&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For INP problems:&lt;/p&gt;

&lt;p&gt;Interaction&lt;br&gt;
↓&lt;br&gt;
Event handler&lt;br&gt;
↓&lt;br&gt;
JavaScript execution&lt;br&gt;
↓&lt;br&gt;
Rendering&lt;br&gt;
↓&lt;br&gt;
Paint&lt;/p&gt;

&lt;p&gt;Use the Performance panel.&lt;/p&gt;

&lt;p&gt;Look for long tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6 — Inspect layout shifts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For CLS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which element moved?&lt;/li&gt;
&lt;li&gt;Why did it move?&lt;/li&gt;
&lt;li&gt;Could space have been reserved?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 7 — Fix the root cause&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't just optimize whatever is easiest.&lt;/p&gt;

&lt;p&gt;If the problem is:&lt;/p&gt;

&lt;p&gt;Slow server&lt;/p&gt;

&lt;p&gt;don't spend three days optimizing JavaScript.&lt;/p&gt;

&lt;p&gt;If the problem is:&lt;/p&gt;

&lt;p&gt;Huge JavaScript task&lt;/p&gt;

&lt;p&gt;don't spend the day compressing images.&lt;/p&gt;

&lt;p&gt;Fix the bottleneck.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 8 — Measure again&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;p&gt;LCP = 4.3s&lt;/p&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;p&gt;LCP = 2.1s&lt;/p&gt;

&lt;p&gt;Now you know the optimization actually worked.&lt;/p&gt;
&lt;h2&gt;
  
  
  Common fixes cheat sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Possible solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Slow LCP&lt;/td&gt;
&lt;td&gt;Improve TTFB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large LCP image&lt;/td&gt;
&lt;td&gt;Compress/resize image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LCP image requested late&lt;/td&gt;
&lt;td&gt;Prioritize/preload appropriately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Render blocked by JS&lt;/td&gt;
&lt;td&gt;Reduce critical JS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large JS bundle&lt;/td&gt;
&lt;td&gt;Code splitting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long JS tasks&lt;/td&gt;
&lt;td&gt;Break work into smaller tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heavy computation&lt;/td&gt;
&lt;td&gt;Web Worker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large React list&lt;/td&gt;
&lt;td&gt;Virtualization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Excessive re-renders&lt;/td&gt;
&lt;td&gt;Optimize component/state boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Images causing CLS&lt;/td&gt;
&lt;td&gt;Set dimensions/aspect ratio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ads causing CLS&lt;/td&gt;
&lt;td&gt;Reserve space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic content causing CLS&lt;/td&gt;
&lt;td&gt;Reserve layout space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Font-related CLS&lt;/td&gt;
&lt;td&gt;Optimize font loading/metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Third-party scripts&lt;/td&gt;
&lt;td&gt;Delay/remove non-critical scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  JavaScript performance and Web Vitals are connected
&lt;/h2&gt;

&lt;p&gt;One of the biggest misconceptions is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Web Vitals are just a frontend infrastructure problem."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They're not.&lt;/p&gt;

&lt;p&gt;They're often an application architecture problem.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Large JavaScript bundle&lt;br&gt;
↓&lt;br&gt;
Long parse/compile time&lt;br&gt;
↓&lt;br&gt;
Delayed execution&lt;br&gt;
↓&lt;br&gt;
Delayed rendering&lt;br&gt;
↓&lt;br&gt;
LCP problem&lt;/p&gt;

&lt;p&gt;And:&lt;/p&gt;

&lt;p&gt;Large synchronous task&lt;br&gt;
↓&lt;br&gt;
Main thread blocked&lt;br&gt;
↓&lt;br&gt;
Interaction delayed&lt;br&gt;
↓&lt;br&gt;
INP problem&lt;/p&gt;

&lt;p&gt;And:&lt;/p&gt;

&lt;p&gt;Client-side rendering&lt;br&gt;
↓&lt;br&gt;
Content appears later&lt;br&gt;
↓&lt;br&gt;
Layout changes&lt;br&gt;
↓&lt;br&gt;
CLS problem&lt;/p&gt;

&lt;p&gt;Performance should therefore be considered when designing the application, not only after the application becomes slow.&lt;/p&gt;
&lt;h2&gt;
  
  
  Performance is a system property
&lt;/h2&gt;

&lt;p&gt;A fast website isn't created by one optimization.&lt;/p&gt;

&lt;p&gt;It's usually the result of several layers working together:&lt;br&gt;
                ┌──────────────┐&lt;br&gt;
                │   Backend    │&lt;br&gt;
                └──────┬───────┘&lt;br&gt;
                       ↓&lt;br&gt;
                ┌──────────────┐&lt;br&gt;
                │   Network    │&lt;br&gt;
                └──────┬───────┘&lt;br&gt;
                       ↓&lt;br&gt;
                ┌──────────────┐&lt;br&gt;
                │     HTML     │&lt;br&gt;
                └──────┬───────┘&lt;br&gt;
                       ↓&lt;br&gt;
                ┌──────────────┐&lt;br&gt;
                │     CSS      │&lt;br&gt;
                └──────┬───────┘&lt;br&gt;
                       ↓&lt;br&gt;
                ┌──────────────┐&lt;br&gt;
                │ JavaScript   │&lt;br&gt;
                └──────┬───────┘&lt;br&gt;
                       ↓&lt;br&gt;
                ┌──────────────┐&lt;br&gt;
                │    Render    │&lt;br&gt;
                └──────┬───────┘&lt;br&gt;
                       ↓&lt;br&gt;
                ┌──────────────┐&lt;br&gt;
                │ User Input   │&lt;br&gt;
                └──────────────┘&lt;/p&gt;

&lt;p&gt;Every layer can influence the user experience.&lt;/p&gt;
&lt;h2&gt;
  
  
  The tools I use for Web Vitals
&lt;/h2&gt;

&lt;p&gt;A useful performance toolkit includes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chrome DevTools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Great for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance profiling&lt;/li&gt;
&lt;li&gt;Long tasks&lt;/li&gt;
&lt;li&gt;Layout shifts&lt;/li&gt;
&lt;li&gt;Network analysis&lt;/li&gt;
&lt;li&gt;CPU throttling&lt;/li&gt;
&lt;li&gt;Network throttling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lighthouse&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Useful for controlled lab audits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PageSpeed Insights&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Useful because it combines lab analysis with available real-user data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chrome UX Report&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Useful for understanding real-world performance at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;web-vitals&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Useful for collecting metrics directly from real users in your application.&lt;/p&gt;
&lt;h2&gt;
  
  
  A minimal production setup
&lt;/h2&gt;

&lt;p&gt;You don't need a complicated observability platform to start collecting Web Vitals.&lt;/p&gt;

&lt;p&gt;Start simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onCLS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;web-vitals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reportWebVital&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBeacon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/web-vitals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reportWebVital&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reportWebVital&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;onCLS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reportWebVital&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your backend can then store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;metric&lt;/li&gt;
&lt;li&gt;value&lt;/li&gt;
&lt;li&gt;route&lt;/li&gt;
&lt;li&gt;device&lt;/li&gt;
&lt;li&gt;browser&lt;/li&gt;
&lt;li&gt;release&lt;/li&gt;
&lt;li&gt;timestamp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have that data, you can build dashboards around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  One final piece of advice
&lt;/h2&gt;

&lt;p&gt;Don't treat Web Vitals as a score you need to make green.&lt;/p&gt;

&lt;p&gt;Treat them as diagnostic signals.&lt;/p&gt;

&lt;p&gt;If you see:&lt;/p&gt;

&lt;p&gt;LCP = 4.5s&lt;/p&gt;

&lt;p&gt;don't ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do I make LCP smaller?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why did the user have to wait 4.5 seconds before the main content appeared?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you see:&lt;/p&gt;

&lt;p&gt;INP = 600ms&lt;/p&gt;

&lt;p&gt;don't ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do I improve INP?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What did the browser spend 600ms doing after the user's interaction?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you see:&lt;/p&gt;

&lt;p&gt;CLS = 0.3&lt;/p&gt;

&lt;p&gt;don't ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do I reduce CLS?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What moved, and why didn't we reserve its space?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That change in mindset makes performance optimization much more systematic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Web Vitals give us a common language for talking about user experience.&lt;/p&gt;

&lt;p&gt;The three Core Web Vitals answer three important questions:&lt;/p&gt;

&lt;p&gt;LCP&lt;br&gt;
↓&lt;br&gt;
How quickly does the main content appear?&lt;/p&gt;

&lt;p&gt;INP&lt;br&gt;
↓&lt;br&gt;
How quickly does the page respond to interaction?&lt;/p&gt;

&lt;p&gt;CLS&lt;br&gt;
↓&lt;br&gt;
Does the page remain visually stable?&lt;/p&gt;

&lt;p&gt;But measuring the numbers is only the first step.&lt;/p&gt;

&lt;p&gt;The real engineering workflow is:&lt;/p&gt;

&lt;p&gt;Measure&lt;br&gt;
↓&lt;br&gt;
Identify the bad metric&lt;br&gt;
↓&lt;br&gt;
Find the responsible element/interaction&lt;br&gt;
↓&lt;br&gt;
Trace the bottleneck&lt;br&gt;
↓&lt;br&gt;
Fix the root cause&lt;br&gt;
↓&lt;br&gt;
Measure again&lt;br&gt;
↓&lt;br&gt;
Monitor real users&lt;/p&gt;

&lt;p&gt;That's how Web Vitals become more than a Lighthouse score.&lt;/p&gt;

&lt;p&gt;They become a practical tool for building faster, more responsive, and more reliable web applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://web.dev/vitals/" rel="noopener noreferrer"&gt;Web Vitals — web.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/vitals" rel="noopener noreferrer"&gt;Core Web Vitals — web.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GoogleChrome/web-vitals" rel="noopener noreferrer"&gt;Web Vitals JavaScript library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/debug-performance-in-the-field" rel="noopener noreferrer"&gt;Debug performance in the field — web.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;PageSpeed Insights&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you found this useful, I'd love to hear how you currently measure and debug performance in your applications.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>performance</category>
      <category>frontend</category>
    </item>
    <item>
      <title>react-query-filters: A Headless React Hook for URL-Synced Search, Filters, Sorting &amp; Pagination</title>
      <dc:creator>AMAL</dc:creator>
      <pubDate>Mon, 07 Sep 2026 09:41:24 +0000</pubDate>
      <link>https://dev.to/amalmktk/react-query-filters-a-headless-react-hook-for-url-synced-search-filters-sorting-pagination-4ea1</link>
      <guid>https://dev.to/amalmktk/react-query-filters-a-headless-react-hook-for-url-synced-search-filters-sorting-pagination-4ea1</guid>
      <description>&lt;p&gt;Every React project I've worked on eventually needs the same thing: search, filters, sorting, and pagination that survive a page refresh and can be shared through a URL.&lt;/p&gt;

&lt;p&gt;I've found myself repeatedly writing useSearchParams and URLSearchParams boilerplate to handle this.&lt;/p&gt;

&lt;p&gt;So I built react-query-filters — a small, headless hook for managing query state and keeping it synchronized with the URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;setFilter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;setSort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;setPage&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQueryFilters&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your URL automatically stays in sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/products
    ↓
/products?search=laptop&amp;amp;status=active&amp;amp;sort=price:desc&amp;amp;page=2

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No manual URLSearchParams juggling, and browser back/forward navigation works naturally.&lt;/p&gt;

&lt;p&gt;Why I built it&lt;br&gt;
🎯 Headless — no UI components or design system imposed&lt;br&gt;
🔗 URL-synced — state survives refresh and is easily shareable&lt;br&gt;
⚛️ React-friendly — simple hooks-based API&lt;br&gt;
🧩 Framework-agnostic core — usable with frameworks such as Next.js&lt;br&gt;
📅 Date &amp;amp; range filters&lt;br&gt;
📦 Zero runtime dependencies&lt;br&gt;
🔄 Browser history support&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal&lt;/strong&gt;&lt;br&gt;
The goal isn't to build another UI filtering library.&lt;/p&gt;

&lt;p&gt;It's to provide a small layer for URL query-state management while leaving the UI completely up to you.&lt;/p&gt;

&lt;p&gt;You can use it with your existing components, whether that's your own design system, shadcn/ui, MUI, or something else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Early project&lt;/strong&gt;&lt;br&gt;
This is still an early project — currently v1.1.0.&lt;/p&gt;

&lt;p&gt;I'd especially love feedback on the API design.&lt;/p&gt;

&lt;p&gt;Is the API intuitive?&lt;/p&gt;

&lt;p&gt;Are there patterns you would handle differently?&lt;/p&gt;

&lt;p&gt;Are there edge cases around URL state that you think the library should support?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;br&gt;
GitHub:&lt;br&gt;
&lt;a href="https://github.com/Amalmktk/react-query-filters" rel="noopener noreferrer"&gt;https://github.com/Amalmktk/react-query-filters&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;npm:&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/react-query-filters" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/react-query-filters&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-query-filters

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks for checking it out! 🚀&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
