"use client"; import { useEffect, useCallback } from "react"; // Extend Window interface for gtag declare global { interface Window { gtag?: (command: string, targetId: string, config: any) => void; } } interface PerformanceMetrics { FCP: number | null; LCP: number | null; FID: number | null; CLS: number | null; TTFB: number | null; } export const usePerformance = () => { const reportMetric = useCallback((name: string, value: number) => { // Send to analytics service if (typeof window !== "undefined" && window.gtag) { window.gtag("event", name, { value: Math.round(name === "CLS" ? value * 1000 : value), metric_id: name, metric_value: value, metric_delta: 0, }); } // Log to console in development if (process.env.NODE_ENV === "development") { console.log(`Performance Metric - ${name}:`, value); } }, []); useEffect(() => { if (typeof window === "undefined") return; // First Contentful Paint const fcpObserver = new PerformanceObserver((list) => { const entries = list.getEntries(); const fcp = entries[entries.length - 1]; if (fcp) { reportMetric("FCP", fcp.startTime); } }); fcpObserver.observe({ entryTypes: ["paint"] }); // Largest Contentful Paint const lcpObserver = new PerformanceObserver((list) => { const entries = list.getEntries(); const lcp = entries[entries.length - 1]; if (lcp) { reportMetric("LCP", lcp.startTime); } }); lcpObserver.observe({ entryTypes: ["largest-contentful-paint"] }); // First Input Delay const fidObserver = new PerformanceObserver((list) => { const entries = list.getEntries(); entries.forEach((entry) => { const firstInputEntry = entry as PerformanceEventTiming; if (firstInputEntry.processingStart) { const fid = firstInputEntry.processingStart - firstInputEntry.startTime; reportMetric("FID", fid); } }); }); fidObserver.observe({ entryTypes: ["first-input"] }); // Cumulative Layout Shift let clsValue = 0; const clsObserver = new PerformanceObserver((list) => { const entries = list.getEntries(); entries.forEach((entry: any) => { if (!entry.hadRecentInput) { clsValue += entry.value; } }); }); clsObserver.observe({ entryTypes: ["layout-shift"] }); // Time to First Byte const navigationEntry = performance.getEntriesByType("navigation")[0] as PerformanceNavigationTiming; if (navigationEntry) { const ttfb = navigationEntry.responseStart - navigationEntry.requestStart; reportMetric("TTFB", ttfb); } // Report CLS on page unload const handleBeforeUnload = () => { if (clsValue > 0) { reportMetric("CLS", clsValue); } }; window.addEventListener("beforeunload", handleBeforeUnload); return () => { fcpObserver.disconnect(); lcpObserver.disconnect(); fidObserver.disconnect(); clsObserver.disconnect(); window.removeEventListener("beforeunload", handleBeforeUnload); }; }, [reportMetric]); return { reportMetric, }; };