"use client"; import React, { useState, useEffect, memo } from "react"; const WebVitalsDashboard = memo(() => { const [vitals, setVitals] = useState({ lcp: { value: 0, rating: "unknown" }, fid: { value: 0, rating: "unknown" }, cls: { value: 0, rating: "unknown" }, fcp: { value: 0, rating: "unknown" }, ttfb: { value: 0, rating: "unknown" }, }); const [metrics, setMetrics] = useState({}); const [loading, setLoading] = useState(true); useEffect(() => { // Fetch Web Vitals data from API const fetchVitals = async () => { try { const response = await fetch("/api/web-vitals"); const data = await response.json(); setMetrics(data.metrics || {}); } catch (error) { console.error("Error fetching web vitals:", error); } finally { setLoading(false); } }; fetchVitals(); // Set up Web Vitals tracking if (typeof window !== "undefined") { import("web-vitals").then( ({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { // Track Largest Contentful Paint getLCP((metric) => { setVitals((prev) => ({ ...prev, lcp: { value: Math.round(metric.value), rating: metric.rating, }, })); }); // Track First Input Delay getFID((metric) => { setVitals((prev) => ({ ...prev, fid: { value: Math.round(metric.value), rating: metric.rating, }, })); }); // Track Cumulative Layout Shift getCLS((metric) => { setVitals((prev) => ({ ...prev, cls: { value: Math.round(metric.value * 1000) / 1000, rating: metric.rating, }, })); }); // Track First Contentful Paint getFCP((metric) => { setVitals((prev) => ({ ...prev, fcp: { value: Math.round(metric.value), rating: metric.rating, }, })); }); // Track Time to First Byte getTTFB((metric) => { setVitals((prev) => ({ ...prev, ttfb: { value: Math.round(metric.value), rating: metric.rating, }, })); }); }, ); } }, []); const getRatingColor = (rating) => { switch (rating) { case "good": return "text-green-600 bg-green-50"; case "needs-improvement": return "text-yellow-600 bg-yellow-50"; case "poor": return "text-red-600 bg-red-50"; default: return "text-gray-600 bg-gray-50"; } }; const getRatingIcon = (rating) => { switch (rating) { case "good": return "✅"; case "needs-improvement": return "⚠️"; case "poor": return "❌"; default: return "❓"; } }; const formatValue = (metric, value) => { if (metric === "cls") { return value.toFixed(3); } return `${value}ms`; }; if (loading) { return (
{[1, 2, 3, 4, 5].map((i) => (
))}
); } return (

Web Vitals Dashboard

{Object.entries(vitals).map(([metric, data]) => (

{metric.toUpperCase()}

{getRatingIcon(data.rating)}
Value: {formatValue(metric, data.value)}
Rating: {data.rating.replace("-", " ")}
))}
{/* Historical Metrics */} {Object.keys(metrics).length > 0 && (

Historical Metrics

{Object.entries(metrics).map(([metric, data]) => (

{metric.toUpperCase()}

Count: {data.count}
Average: {formatValue(metric, data.average)}
Range: {formatValue(metric, data.min)} -{" "} {formatValue(metric, data.max)}
Good: {data.goodCount} Needs Improvement: {data.needsImprovementCount} Poor: {data.poorCount}
))}
)} {/* Performance Guidelines */}

Performance Guidelines

); }); WebVitalsDashboard.displayName = "WebVitalsDashboard"; export default WebVitalsDashboard;