kontenhumas-fe/hooks/use-performance.ts

114 lines
3.2 KiB
TypeScript

"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,
};
};