# LCP optimization reference

## What is LCP?

Largest Contentful Paint (LCP) measures when the largest content element in the viewport becomes visible. This is typically:

- An `<img>` element
- An `<image>` element inside `<svg>`
- A `<video>` element with poster image
- An element with a background image via `url()`
- A block-level element containing text nodes

## LCP timeline

```
[  Server Response  ][  Resource Load  ][  Render  ]
       TTFB              Download         Paint
       └─────────────────────────────────────┘
                         LCP Time
```

## Detailed optimizations

### 1. Server response time (TTFB)

Target: < 800ms

**Causes:**
- Slow server/database queries
- No CDN/edge caching
- Inefficient backend code
- Cold starts (serverless)

**Solutions:**
```javascript
// Use edge functions for dynamic content
// Vercel example
export const config = { runtime: 'edge' };

// Use stale-while-revalidate caching
// Cache-Control header
res.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate=300');
```

### 2. Resource load time

**For images:**
```html
<!-- Preload LCP image -->
<link rel="preload" as="image" href="/hero.webp" 
      imagesrcset="/hero-400.webp 400w, /hero-800.webp 800w"
      imagesizes="100vw"
      fetchpriority="high">

<!-- Modern format with fallback -->
<picture>
  <source srcset="/hero.avif" type="image/avif">
  <source srcset="/hero.webp" type="image/webp">
  <img src="/hero.jpg" width="1200" height="600" 
       fetchpriority="high" alt="Hero">
</picture>
```

**For text (web fonts):**
```css
@font-face {
  font-family: 'Heading';
  src: url('/fonts/heading.woff2') format('woff2');
  font-display: swap; /* Show fallback immediately */
}
```

### 3. Render blocking resources

**Critical CSS pattern:**
```html
<head>
  <!-- Inline critical CSS -->
  <style>
    /* Only above-fold styles, < 14KB */
    .hero { /* ... */ }
    .nav { /* ... */ }
  </style>
  
  <!-- Defer non-critical CSS -->
  <link rel="preload" href="/styles.css" as="style" 
        onload="this.onload=null;this.rel='stylesheet'">
</head>
```

**Defer JavaScript:**
```html
<!-- ❌ Blocks parsing -->
<script src="/app.js"></script>

<!-- ✅ Deferred (runs after HTML parsed) -->
<script defer src="/app.js"></script>

<!-- ✅ Module (deferred by default) -->
<script type="module" src="/app.mjs"></script>
```

### 4. Client-side rendering

**Problem:** Content not in initial HTML.

**Solutions:**

**Server-side rendering (SSR):**
```javascript
// Next.js
export async function getServerSideProps() {
  const data = await fetchHeroContent();
  return { props: { hero: data } };
}
```

**Static site generation (SSG):**
```javascript
// Next.js
export async function getStaticProps() {
  const data = await fetchHeroContent();
  return { props: { hero: data }, revalidate: 3600 };
}
```

**Streaming SSR:**
```jsx
// React 18+
import { Suspense } from 'react';

function Page() {
  return (
    <Suspense fallback={<HeroSkeleton />}>
      <Hero />
    </Suspense>
  );
}
```

## Framework-specific tips

### Next.js
```jsx
import Image from 'next/image';

// LCP image with priority
<Image 
  src="/hero.jpg"
  priority
  fill
  sizes="100vw"
  alt="Hero"
/>
```

### Nuxt
```vue
<NuxtImg
  src="/hero.jpg"
  preload
  loading="eager"
  sizes="100vw"
/>
```

### Astro
```astro
---
import { Image } from 'astro:assets';
import hero from '../assets/hero.jpg';
---
<Image 
  src={hero} 
  loading="eager" 
  decoding="sync"
  alt="Hero" 
/>
```

## Debugging LCP

```javascript
// Identify LCP element
new PerformanceObserver((entryList) => {
  const entries = entryList.getEntries();
  const lastEntry = entries[entries.length - 1];
  
  console.log('LCP:', {
    element: lastEntry.element,
    time: lastEntry.startTime,
    size: lastEntry.size,
    url: lastEntry.url,
    renderTime: lastEntry.renderTime,
    loadTime: lastEntry.loadTime
  });
}).observe({ type: 'largest-contentful-paint', buffered: true });
```

## Common issues

| Issue | Impact | Fix |
|-------|--------|-----|
| No preload for LCP image | +500-1000ms | Add `<link rel="preload">` |
| Large unoptimized image | +300-800ms | Compress, use WebP/AVIF |
| Render-blocking CSS | +200-500ms | Inline critical CSS |
| Slow TTFB | +300-2000ms | CDN, edge caching |
| Client-rendered content | +500-2000ms | SSR/SSG |
