{"id":"rrrwfdeg06uj3bg","title":"How to Fix LCP Over 2.5 Seconds on a React Site","slug":"fix-lcp-over-2-5-seconds-react","summary":"LCP over 2.5 seconds kills user experience and SEO. I've tackled this on React sites many times. Here’s my no-nonsense approach to finding the bottleneck and shipping real fixes that stick. We'll look at images, rendering, and script loading.","imageUrl":"https://briancrabtree.me/images/journal-fix-lcp-over-2-5-seconds-react.webp","category":"Performance","date":"2026-06-16T18:00:00.000Z","featured":false,"likes":4,"author":"brian@briancrabtree.me","content":"<h2>Understanding LCP on React</h2>\n\n<p>Largest Contentful Paint (LCP) is a Core Web Vital that measures how quickly the largest image or text block renders in the viewport. When LCP creeps over 2.5 seconds, users get impatient and search rankings suffer. For a React site, this often means your JavaScript is delaying critical content.</p>\n\n<p>Pinpointing how to fix LCP over 2.5 seconds on a React site isn't always straightforward. The root cause usually involves a mix of slow image loading, render-blocking scripts, or client-side rendering delays. We need to be systematic about diagnosis before we can apply effective fixes.</p>\n\n<p>I approach LCP as a critical path problem. What's the biggest element? What's stopping it from painting sooner? React's client-side hydration model can exacerbate these issues if not managed carefully. Every millisecond counts from request to paint.</p>\n\n<h2>Identifying the Largest Contentful Element</h2>\n\n<p>First, you need to know what element is actually the LCP candidate. Google Lighthouse and PageSpeed Insights are your best friends here. They'll tell you which specific element is holding things up, whether it's an image, a video poster, or a large block of text.</p>\n\n<p>Without this clarity, you're just guessing. I typically run Lighthouse locally first, then check PageSpeed Insights for real-world CrUX data. If the field data is consistently bad, then you definitely have a problem your users are seeing.</p>\n\n<p>Sometimes the LCP element changes based on viewport size or content. Always test across common device breakpoints. A hero image on desktop might be a text block on mobile, requiring different optimization strategies for each.</p>\n\n<h2>Optimizing Images for LCP</h2>\n\n<p>The most common LCP culprit on React sites is a poorly optimized image. Large, uncompressed hero images block rendering. I immediately check for correct sizing, modern formats like WebP, and appropriate compression levels.</p>\n\n<p>Using `srcset` and `sizes` attributes is non-negotiable for responsive images. This ensures the browser only downloads the image resolution it needs for the current viewport, drastically cutting down on bytes. Without them, you're shipping megabytes to phone users who only needed kilobytes.</p>\n\n<p>Also, ensure images above the fold are not lazy-loaded. `loading=\"eager\"` and `fetchpriority=\"high\"` are key hints to the browser to prioritize these critical resources. Don't defer what the user needs to see immediately.</p>\n\n<h2>Shifting Rendering Strategy</h2>\n\n<p>Client-side rendering (CSR) with React can delay LCP significantly. The browser downloads your HTML, then JavaScript, then React boots up, fetches data, and *then* renders the LCP element. That's a lot of steps before paint.</p>\n\n<p>For critical pages, server-side rendering (SSR) or static site generation (SSG) with a framework like Next.js or Remix is often the best solution. The LCP content is baked directly into the initial HTML response, ready for immediate display.</p>\n\n<p>This drastically reduces the time to first paint and LCP. While it adds server complexity, the performance gains for user-facing content are usually worth the trade-off. It ships fully formed HTML, which is what browsers are designed to render fast.</p>\n\n<h2>Taming Render-Blocking Assets</h2>\n\n<p>Render-blocking CSS and JavaScript are major LCP killers. The browser stops rendering until it has processed these files. For React apps, this means your main bundle, and sometimes even component-specific CSS, can delay everything.</p>\n\n<p>I extract and inline critical CSS needed for the above-the-fold content directly into the HTML. This gets the basic layout and LCP element styled without waiting for a separate stylesheet download. The rest can be loaded asynchronously.</p>\n\n<p>Similarly, deferring non-critical JavaScript until after the initial render is crucial. Attributes like `defer` and `async` can help, but sometimes more advanced techniques like code splitting and dynamic imports are required. I wrote more on managing non-critical JS at /journal/defer-non-critical-javascript/.</p>\n\n<h2>Managing Web Fonts and Third Parties</h2>\n\n<p>Custom web fonts can also cause LCP delays if not handled correctly. The browser won't render text until the font file is downloaded. Using `font-display: swap` prevents this by rendering text with a fallback system font immediately, then swapping to the custom font once it loads.</p>\n\n<p>Self-hosting fonts is generally faster than relying on third-party CDN's. This eliminates an extra DNS lookup and connection overhead. Always preload your critical fonts using ``.</p>\n\n<p>Third-party scripts, like analytics or ad libraries, are notorious for blocking rendering. Audit these scripts. Load them with `async` or `defer`, or consider if they truly need to run at page load. Sometimes, they can be loaded completely client-side after initial LCP.</p>\n\n<h2>What I do next</h2>\n\n<p>After implementing these fixes, the work isn't over. Performance is a continuous process. Monitor your LCP metrics using Lighthouse and PageSpeed Insights regularly, especially after new deployments or content changes. Small regressions add up quickly.</p>\n\n<p>Keep an eye on the specific LCP element. As you optimize, a different element might become the largest and introduce a new bottleneck. This iterative process is how you maintain a fast user experience over time.</p>\n\n<p>For more details on sizing images correctly and optimizing LCP with `srcset`, check out my post at /journal/image-srcset-lcp-sizing/. The core principles are the same: identify, optimize, and verify.</p>","tags":["lcp","core-web-vitals","react"],"views":7}