Skip to content
Development
Agent

core-web-vitals

Comprehensive Core Web Vitals monitoring and optimization patterns.

From plugin
vexjoy-agent
449198 skills198 agents12 commands83 hooks

Claude Code

$ npx -y skills add notque/vexjoy-agent --agent claude-code

Agent definition

core-web-vitals.md

Core Web Vitals Implementation

Comprehensive Core Web Vitals monitoring and optimization patterns.

Core Web Vitals Thresholds

| Metric | Good | Needs Improvement | Poor | |--------|------|-------------------|------| | LCP (Largest Contentful Paint) | ≤2.5s | 2.5s - 4.0s | >4.0s | | FID (First Input Delay) | ≤100ms | 100ms - 300ms | >300ms | | CLS (Cumulative Layout Shift) | ≤0.1 | 0.1 - 0.25 | >0.25 | | FCP (First Contentful Paint) | ≤1.8s | 1.8s - 3.0s | >3.0s | | TTFB (Time to First Byte) | ≤800ms | 800ms - 1800ms | >1800ms |

Implementation Example

// Comprehensive Core Web Vitals monitoring
import { getCLS, getFCP, getFID, getLCP, getTTFB } from 'web-vitals'

interface PerformanceConfig {
  enableAnalytics: boolean
  sampleRate: number
  reportingEndpoint: string
  thresholds: {
    lcp: { good: number; poor: number }
    fid: { good: number; poor: number }
    cls: { good: number; poor: number }
    fcp: { good: number; poor: number }
    ttfb: { good: number; poor: number }
  }
}

const performanceConfig: PerformanceConfig = {
  enableAnalytics: process.env.NODE_ENV === 'production',
  sampleRate: 0.1, // 10% sampling
  reportingEndpoint: '/api/performance',
  thresholds: {
    lcp: { good: 2500, poor: 4000 },
    fid: { good: 100, poor: 300 },
    cls: { good: 0.1, poor: 0.25 },
    fcp: { good: 1800, poor: 3000 },
    ttfb: { good: 800, poor: 1800 }
  }
}

class CoreWebVitalsMonitor {
  private config: PerformanceConfig
  private metrics: Map<string, any> = new Map()
  private queue: any[] = []

  constructor(config: PerformanceConfig) {
    this.config = config
    this.initializeMonitoring()
  }

  private initializeMonitoring(): void {
    // Only monitor if sampling allows
    if (Math.random() > this.config.sampleRate) return

    // Monitor Core Web Vitals
    getCLS(this.handleMetric.bind(this), true)
    getFCP(this.handleMetric.bind(this))
    getFID(this.handleMetric.bind(this))
    getLCP(this.handleMetric.bind(this), true)
    getTTFB(this.handleMetric.bind(this))

    this.setupReporting()
  }

  private handleMetric(metric: any): void {
    const performanceMetric = {
      name: metric.name,
      value: metric.value,
      rating: this.getRating(metric.name, metric.value),
      delta: metric.delta || 0,
      id: metric.id,
      navigationType: this.getNavigationType()
    }

    this.metrics.set(metric.name, performanceMetric)
    this.queue.push(performanceMetric)

    // Immediate reporting for poor metrics
    if (performanceMetric.rating === 'poor') {
      this.reportMetrics([performanceMetric], 'urgent')
    }
  }

  private getRating(name: string, value: number): 'good' | 'needs-improvement' | 'poor' {
    const thresholds = this.config.thresholds[name as keyof typeof this.config.thresholds]
    if (!thresholds) return 'good'

    if (value <= thresholds.good) return 'good'
    if (value <= thresholds.poor) return 'needs-improvement'
    return 'poor'
  }

  private setupReporting(): void {
    // Report metrics periodically
    setInterval(() => {
      if (this.queue.length > 0) {
        this.reportMetrics([...this.queue])
        this.queue = []
      }
    }, 30000) // Every 30 seconds

    // Report on page unload
    window.addEventListener('beforeunload', () => {
      if (this.queue.length > 0) {
        this.reportMetrics([...this.queue], 'beacon')
      }
    })

    // Report on visibility change
    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'hidden' && this.queue.length > 0) {
        this.reportMetrics([...this.queue], 'beacon')
        this.queue = []
      }
    })
  }

  private async reportMetrics(metrics: any[], method: 'fetch' | 'beacon' | 'urgent' = 'fetch'): Promise<void> {
    if (!this.config.enableAnalytics) return

    try {
      const payload = {
        metrics,
        timestamp: Date.now(),
        url: window.location.href,
        userAgent: navigator.userAgent,
        connection: this.getConnectionInfo(),
        viewport: {
          width: window.innerWidth,
          height: window.innerHeight
        }
      }

      if (method === 'beacon' && 'sendBeacon' in navigator) {
        navigator.sendBeacon(this.config.reportingEndpoint, JSON.stringify(payload))
      } else {
        await fetch(this.config.reportingEndpoint, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(payload),
          keepalive: method === 'beacon'
        })
      }
    } catch (error) {
      console.error('Failed to report performance metrics:', error)
    }
  }

  private getConnectionInfo(): any {
    if ('connection' in navigator) {
      const conn = (navigator as any).connection
      return {
        effectiveType: conn.effectiveType,
        downlink: conn.downlink,
        rtt: conn.rtt,
        saveData: conn.saveData
      }
    }
    return {}
  }

  private getNavigationType(): string {
    if ('navigation' in performance) {
      const nav = performance.navigation as any
      switch (nav.type) {
        case 0: return 'navigate'
        case 1: return 'reload'
        case 2: return 'back_forward'
        default: return 'unknown'
      }
    }
    return 'unknown'
  }

  getCurrentMetrics(): Record<string, number> {
    const current: Record<string, number> = {}
    this.metrics.forEach((metric, name) => {
      current[name] = metric.value
    })
    return current
  }

  getPerformanceScore(): number {
    const scores = {
      lcp: this.getMetricScore('LCP'),
      fid: this.getMetricScore('FID'),
      cls: this.getMetricScore('CLS')
    }

    return (scores.lcp + scores.fid + scores.cls) / 3
  }

  private getMetricScore(name: string): number {
    const metric = this.metrics.get(name)
    if (!metric) return 100

    switch (metric.rating) {
      case 'good': return 100
      case 'needs-improvement': return 75
      case 'poor': return 25
      default: return 100
    }
  }
}
Read more
@notque
Ships withvexjoy-agent

Essays and writing behind this toolkit live at vexjoy.com. VexJoy Agent connects plain-English requests to specialist agents, skills, and workflows. /do selects the knowledge and tools needed for your task.

44964 skills198 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
449
Star history of notque/vexjoy-agent on GitHub, a chart by star-history.com

Other agents on vexjoy-agent.

modules

modules

Agent

**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes…

@notque@notqueInstall Agent
testing

testing

Agent

**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**:…

@notque@notqueInstall Agent
communication-patterns

communication-patterns

Agent

**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what…

@notque@notqueInstall Agent

More agents from other plugins.

a11y-architect
Auto-invoked

a11y-architect

Agent

Accessibility Architect specializing in WCAG 2.2 compliance for Web and Native platforms. Use PROACTIVELY when designing UI…

ecc13d293k
@affaan-m@affaan-mInstall Agent
analyze-pass-impact

analyze-pass-impact

Agent

Analyzes how a specific topic affects a group of compiler passes. Used by the /plan-update skill to parallelize research across…

react2d252k
@facebook@facebookInstall Agent
orchestrator

orchestrator

Agent

- If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as…

codex2d135k
@openai@openaiInstall Agent
cavecrew-builder

cavecrew-builder

Agent

Surgical 1-2 file edit. Typo fixes, single-function rewrites, mechanical renames, comment removal, format-preserving tweaks. Hard…

caveman3d119k
@juliusbrussee@juliusbrusseeInstall Agent

Questions about core-web-vitals.

Which coding agents can use core-web-vitals?
core-web-vitals installs in Claude Code and Cursor: each loads subagents from .claude/agents, so copy the file there. Flowy's auto-invocation runs in Claude Code only.