feat: implement Umami page speed tracking via Web Vitals
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Successful in 2m25s
Build & Deploy / 🏗️ Build (push) Successful in 3m59s
Build & Deploy / 🚀 Deploy (push) Successful in 15s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 4m55s
Build & Deploy / 🔔 Notify (push) Successful in 2s
Nightly QA / call-qa-workflow (push) Failing after 45s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Successful in 2m25s
Build & Deploy / 🏗️ Build (push) Successful in 3m59s
Build & Deploy / 🚀 Deploy (push) Successful in 15s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 4m55s
Build & Deploy / 🔔 Notify (push) Successful in 2s
Nightly QA / call-qa-workflow (push) Failing after 45s
- Add WebVitalsTracker component using useReportWebVitals - Report LCP, CLS, FID, FCP, TTFB, and INP as Umami events - Include rating (good/needs-improvement/poor) for meaningful metrics
This commit is contained in:
@@ -9,6 +9,9 @@ const DynamicAnalyticsProvider = dynamic(() => import('./AnalyticsProvider'), {
|
|||||||
const DynamicScrollDepthTracker = dynamic(() => import('./ScrollDepthTracker'), {
|
const DynamicScrollDepthTracker = dynamic(() => import('./ScrollDepthTracker'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
|
const DynamicWebVitalsTracker = dynamic(() => import('./WebVitalsTracker'), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
export default function AnalyticsShell() {
|
export default function AnalyticsShell() {
|
||||||
const [shouldLoad, setShouldLoad] = useState(false);
|
const [shouldLoad, setShouldLoad] = useState(false);
|
||||||
@@ -34,6 +37,7 @@ export default function AnalyticsShell() {
|
|||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<DynamicAnalyticsProvider />
|
<DynamicAnalyticsProvider />
|
||||||
<DynamicScrollDepthTracker />
|
<DynamicScrollDepthTracker />
|
||||||
|
<DynamicWebVitalsTracker />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
54
components/analytics/WebVitalsTracker.tsx
Normal file
54
components/analytics/WebVitalsTracker.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useReportWebVitals } from 'next/web-vitals';
|
||||||
|
import { useAnalytics } from './useAnalytics';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebVitalsTracker component.
|
||||||
|
*
|
||||||
|
* Captures Next.js Web Vitals and reports them to Umami as custom events.
|
||||||
|
* This provides "meaningful" page speed tracking by measuring real user
|
||||||
|
* experiences (LCP, CLS, INP, etc.).
|
||||||
|
*/
|
||||||
|
export default function WebVitalsTracker() {
|
||||||
|
const { trackEvent } = useAnalytics();
|
||||||
|
|
||||||
|
useReportWebVitals((metric) => {
|
||||||
|
const { name, value, id, label } = metric;
|
||||||
|
|
||||||
|
// Determine rating (simplified version of web-vitals standards)
|
||||||
|
let rating: 'good' | 'needs-improvement' | 'poor' = 'good';
|
||||||
|
|
||||||
|
if (name === 'LCP') {
|
||||||
|
if (value > 4000) rating = 'poor';
|
||||||
|
else if (value > 2500) rating = 'needs-improvement';
|
||||||
|
} else if (name === 'CLS') {
|
||||||
|
if (value > 0.25) rating = 'poor';
|
||||||
|
else if (value > 0.1) rating = 'needs-improvement';
|
||||||
|
} else if (name === 'FID') {
|
||||||
|
if (value > 300) rating = 'poor';
|
||||||
|
else if (value > 100) rating = 'needs-improvement';
|
||||||
|
} else if (name === 'FCP') {
|
||||||
|
if (value > 3000) rating = 'poor';
|
||||||
|
else if (value > 1800) rating = 'needs-improvement';
|
||||||
|
} else if (name === 'TTFB') {
|
||||||
|
if (value > 1500) rating = 'poor';
|
||||||
|
else if (value > 800) rating = 'needs-improvement';
|
||||||
|
} else if (name === 'INP') {
|
||||||
|
if (value > 500) rating = 'poor';
|
||||||
|
else if (value > 200) rating = 'needs-improvement';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report to Umami
|
||||||
|
trackEvent('web-vital', {
|
||||||
|
metric: name,
|
||||||
|
value: Math.round(name === 'CLS' ? value * 1000 : value), // CLS is a score, multiply by 1000 to keep as integer if preferred
|
||||||
|
rating,
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
path: typeof window !== 'undefined' ? window.location.pathname : undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user