Skip to content

Web Vitals

Monitor Core Web Vitals and real user performance metrics.

Overview

Web Vitals are Google's metrics for measuring real-world user experience. Nadi automatically captures these metrics to help you understand and improve your site's performance.

Core Web Vitals

MetricFull NameGoodNeeds ImprovementPoor
LCPLargest Contentful Paint≤ 2.5s2.5s - 4s> 4s
FIDFirst Input Delay≤ 100ms100ms - 300ms> 300ms
CLSCumulative Layout Shift≤ 0.10.1 - 0.25> 0.25

Additional Metrics

MetricFull NameDescription
FCPFirst Contentful PaintTime to first content render
TTFBTime to First ByteServer response time
INPInteraction to Next PaintOverall responsiveness

Configuration

Enable Web Vitals

javascript
import { init } from '@nadi-pro/browser'

init({
  appKey: 'your-app-key',
  enableWebVitals: true,
})

Select Specific Metrics

javascript
init({
  appKey: 'your-app-key',
  enableWebVitals: true,
  webVitals: ['LCP', 'FID', 'CLS'], // Only these metrics
})

Configure Reporting

javascript
init({
  appKey: 'your-app-key',
  enableWebVitals: true,

  // Report after collecting this many metrics
  webVitalsReportThreshold: 3,

  // Sample rate for Web Vitals (0-1)
  webVitalsSampleRate: 0.1, // 10% of page loads
})

Understanding Each Metric

LCP (Largest Contentful Paint)

Measures loading performance - when the main content becomes visible.

What's measured:

  • <img> elements
  • <video> poster images
  • Elements with background images
  • Text blocks

Optimization tips:

  • Optimize images (compression, format, lazy loading)
  • Use a CDN for static assets
  • Implement preloading for critical resources
  • Minimize render-blocking CSS/JS

FID (First Input Delay)

Measures interactivity - time from first interaction to browser response.

What's measured:

  • Clicks
  • Taps
  • Key presses

Optimization tips:

  • Break up long JavaScript tasks
  • Use web workers for heavy processing
  • Optimize JavaScript execution
  • Implement code splitting

CLS (Cumulative Layout Shift)

Measures visual stability - how much content shifts unexpectedly.

What's measured:

  • Layout shifts without user interaction
  • Shifts of visible content

Optimization tips:

  • Set explicit dimensions for images/videos
  • Reserve space for ads/embeds
  • Avoid inserting content above existing content
  • Use CSS transform for animations

FCP (First Contentful Paint)

Time until first content renders.

Optimization tips:

  • Reduce server response time
  • Eliminate render-blocking resources
  • Preload critical resources

TTFB (Time to First Byte)

Server response time.

Optimization tips:

  • Optimize server-side code
  • Use caching
  • Use a CDN
  • Optimize database queries

Custom Performance Metrics

Track custom performance metrics:

javascript
import { recordMetric } from '@nadi-pro/browser'

// Time a specific operation
const start = performance.now()
await fetchDashboardData()
const duration = performance.now() - start

recordMetric({
  name: 'dashboard_load',
  value: duration,
  unit: 'milliseconds',
  tags: {
    user_type: user.type,
  },
})

Using Performance API

javascript
// Measure with Performance API
performance.mark('api-start')
await apiCall()
performance.mark('api-end')
performance.measure('api-call', 'api-start', 'api-end')

const measure = performance.getEntriesByName('api-call')[0]
recordMetric({
  name: 'api_call_duration',
  value: measure.duration,
})

Viewing Metrics

Dashboard

In the Nadi dashboard:

  1. Navigate to Performance or RUM
  2. View aggregate metrics over time
  3. Filter by page, device, or connection
  4. Identify pages with poor performance

With Error Context

Web Vitals are included with error events:

javascript
{
  "error": { ... },
  "webVitals": {
    "LCP": 2340,
    "FID": 45,
    "CLS": 0.05
  }
}

Segmentation

By Page

javascript
// Metrics are automatically tagged with the current URL
// View per-page metrics in the dashboard

By User Type

javascript
import { setTag } from '@nadi-pro/browser'

// Tag metrics with user information
setTag('user_type', user.subscription)
setTag('country', user.country)

By Device

Automatically captured:

  • Device type (mobile, tablet, desktop)
  • Connection type (4g, 3g, wifi)
  • Browser and version

Performance Budgets

Alert when metrics exceed thresholds:

javascript
import { onWebVitalReport } from '@nadi-pro/browser'

onWebVitalReport((metric) => {
  const thresholds = {
    LCP: 2500,
    FID: 100,
    CLS: 0.1,
  }

  if (metric.value > thresholds[metric.name]) {
    console.warn(`${metric.name} exceeded budget: ${metric.value}`)

    // Send alert or log
    captureMessage(`Performance budget exceeded: ${metric.name}`, 'warning', {
      extra: metric,
    })
  }
})

Debugging Performance

Debug Mode

javascript
init({
  appKey: 'your-app-key',
  enableWebVitals: true,
  debug: true, // Logs metrics to console
})

Manual Metric Inspection

javascript
import { getWebVitals } from '@nadi-pro/browser'

const metrics = getWebVitals()
console.log('Current Web Vitals:', metrics)

Integration with Analytics

Send metrics to other analytics tools:

javascript
import { onWebVitalReport } from '@nadi-pro/browser'

onWebVitalReport((metric) => {
  // Send to Google Analytics
  gtag('event', metric.name, {
    value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
    event_label: metric.id,
    non_interaction: true,
  })
})

Best Practices

Do

  • Monitor all Core Web Vitals
  • Set performance budgets
  • Segment by page and user type
  • Track trends over time

Don't

  • Ignore poor performance pages
  • Over-optimize based on single measurements
  • Forget about mobile users
  • Dismiss small CLS issues

Troubleshooting

Metrics Not Appearing

  1. Check enableWebVitals is true
  2. Verify the page fully loads (metrics fire after page interaction)
  3. Check browser support (LCP/CLS need Chrome 77+)

Inconsistent Values

  • Web Vitals vary based on device and network
  • Focus on percentiles (p75, p95) not averages
  • Check for layout shifts from ads/embeds

Next Steps

Released under the MIT License.