Core Web Vitals
LCP

Largest Contentful Paint (LCP)

Quick check for Largest Contentful Paint (opens in a new tab), a Core Web Vital that measures loading performance. LCP marks when the largest content element becomes visible in the viewport.

LCP Rating Thresholds:

RatingTimeMeaning
🟢 Good≤ 2.5sFast, content appears quickly
🟡 Needs Improvement≤ 4sModerate delay
🔴 Poor> 4sSlow, users may abandon

Need to optimize? Use LCP Sub-Parts to identify which phase (TTFB, load delay, load time, render delay) is causing the bottleneck.

Snippet

// LCP Quick Check
// https://webperf-snippets.nucliweb.net
 
(() => {
  const valueToRating = (ms) =>
    ms <= 2500 ? "good" : ms <= 4000 ? "needs-improvement" : "poor";
 
  const RATING = {
    good: { icon: "🟢", color: "#0CCE6A" },
    "needs-improvement": { icon: "🟡", color: "#FFA400" },
    poor: { icon: "🔴", color: "#FF4E42" },
  };
 
  const getActivationStart = () => {
    const navEntry = performance.getEntriesByType("navigation")[0];
    return navEntry?.activationStart || 0;
  };
 
  const observer = new PerformanceObserver((list) => {
    const entries = list.getEntries();
    const lastEntry = entries[entries.length - 1];
 
    if (!lastEntry) return;
 
    const activationStart = getActivationStart();
    const lcpTime = Math.max(0, lastEntry.startTime - activationStart);
    const rating = valueToRating(lcpTime);
    const { icon, color } = RATING[rating];
 
    console.group(`%cLCP: ${icon} ${(lcpTime / 1000).toFixed(2)}s (${rating})`, `color: ${color}; font-weight: bold; font-size: 14px;`);
 
    // Element info
    const element = lastEntry.element;
    if (element) {
      console.log("");
      console.log("%cLCP Element:", "font-weight: bold;");
 
      // Get element identifier
      let selector = element.tagName.toLowerCase();
      if (element.id) selector = `#${element.id}`;
      else if (element.className && typeof element.className === "string") {
        const classes = element.className.trim().split(/\s+/).slice(0, 2).join(".");
        if (classes) selector = `${element.tagName.toLowerCase()}.${classes}`;
      }
 
      console.log(`   Element: ${selector}`, element);
 
      // Element type and details
      const tagName = element.tagName.toLowerCase();
      if (tagName === "img") {
        console.log(`   Type: Image`);
        console.log(`   URL: ${lastEntry.url || element.src}`);
        if (element.naturalWidth) {
          console.log(`   Dimensions: ${element.naturalWidth}×${element.naturalHeight}`);
        }
      } else if (tagName === "video") {
        console.log(`   Type: Video poster`);
        console.log(`   URL: ${lastEntry.url || element.poster}`);
      } else if (element.style?.backgroundImage) {
        console.log(`   Type: Background image`);
        console.log(`   URL: ${lastEntry.url}`);
      } else {
        console.log(`   Type: ${tagName === "h1" || tagName === "p" ? "Text block" : tagName}`);
      }
 
      // Size
      if (lastEntry.size) {
        console.log(`   Size: ${lastEntry.size.toLocaleString()} px²`);
      }
 
      // Highlight element
      element.style.outline = "3px dashed lime";
      element.style.outlineOffset = "2px";
      console.log("");
      console.log("%c✓ Element highlighted with green dashed outline", "color: #22c55e;");
    }
 
    console.groupEnd();
  });
 
  observer.observe({ type: "largest-contentful-paint", buffered: true });
 
  console.log("%c⏱️ LCP Tracking Active", "font-weight: bold; font-size: 14px;");
  console.log("   LCP may update as larger elements load.");
})();

Understanding LCP

What can be an LCP element:

  • <img> elements
  • <image> inside <svg>
  • <video> poster images
  • Elements with background-image (CSS)
  • Block-level text elements (<p>, <h1>, etc.)

LCP updates until:

  • User interacts (click, scroll, keypress)
  • A larger element renders
  • Page load completes

Common Causes of Slow LCP

CauseSolution
Slow server responseOptimize TTFB, use CDN
Render-blocking resourcesDefer non-critical CSS/JS
Slow resource loadPreload LCP image, optimize size
Client-side renderingUse SSR or prerender

Further Reading