Cours Next.js · Module 8: Déploiement et production

Intégration avec le Web Analytics

13 min de lecture
Dans cette leçon13

Dans le monde des applications web, comprendre le comportement des utilisateurs et mesurer l'efficacité est essentiel pour prendre des décisions commerciales et techniques solides. Intégrer des outils d'analytics à une application Next.js permet de suivre le trafic, le comportement des utilisateurs, les conversions et d'autres indicateurs de performance importants.

Outils d'analytics populaires

Les outils d'analytics les plus couramment utilisés dans les applications Next.js comprennent :

  1. Google Analytics - un outil complet avec un large éventail de fonctionnalités d'analytics
  2. Vercel Analytics - l'analytics natif pour les applications hébergées sur Vercel
  3. Plausible - une alternative open-source respectueuse de la vie privée
  4. Fathom - un système d'analytics simple et axé sur la vie privée
  5. PostHog - une plateforme tout-en-un combinant analytics, sessions utilisateur et fonctionnalités expérimentales
  6. Mixpanel - un analytics avancé axé sur le suivi des événements et les tunnels de conversion
  7. Amplitude - axé sur l'analyse du comportement des utilisateurs et le product analytics

Intégrer Google Analytics 4 (GA4)

Google Analytics est l'outil d'analytics le plus populaire. Voici comment intégrer GA4 à une application Next.js :

1. Configurer Google Analytics

Commence par créer un compte Google Analytics et configurer un flux de données :

  1. Va sur Google Analytics
  2. Crée un nouveau compte et configure un indicateur de site
  3. Configure un flux de données web et note l'ID de mesure (G-XXXXXXXX)

2. Implémentation dans Next.js avec l'App Router

Crée un composant Google Analytics :

1// components/GoogleAnalytics.tsx
2'use client';
3
4import Script from 'next/script';
5
6export default function GoogleAnalytics({ GA_MEASUREMENT_ID }: { GA_MEASUREMENT_ID: string }) {
7  return (
8    <>
9      <Script
10        strategy="afterInteractive"
11        src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
12      />
13      <Script
14        id="google-analytics"
15        strategy="afterInteractive"
16        dangerouslySetInnerHTML={{
17          __html: `
18            window.dataLayer = window.dataLayer || [];
19            function gtag(){dataLayer.push(arguments);}
20            gtag('js', new Date());
21            gtag('config', '${GA_MEASUREMENT_ID}', {
22              page_path: window.location.pathname,
23              cookie_flags: 'SameSite=None;Secure'
24            });
25          `,
26        }}
27      />
28    </>
29  );
30}

Puis ajoute le composant au layout principal de l'application :

1// app/layout.tsx
2import GoogleAnalytics from '@/components/GoogleAnalytics';
3
4export default function RootLayout({
5  children,
6}: {
7  children: React.ReactNode;
8}) {
9  return (
10    <html lang="pl">
11      <body>
12        {process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID && (
13          <GoogleAnalytics GA_MEASUREMENT_ID={process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID} />
14        )}
15        {children}
16      </body>
17    </html>
18  );
19}

Assure-toi d'ajouter la variable d'environnement au fichier .env.local :

1NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

3. Suivre la navigation entre les pages

Dans l'App Router, tu dois implémenter le suivi des changements d'URL, car la navigation côté client ne provoque pas de rechargement complet de la page :

1// hooks/usePageTracking.ts
2'use client';
3
4import { usePathname, useSearchParams } from 'next/navigation';
5import { useEffect } from 'react';
6
7export function usePageTracking() {
8  const pathname = usePathname();
9  const searchParams = useSearchParams();
10
11  useEffect(() => {
12    if (pathname && window.gtag) {
13      let url = window.origin + pathname;
14      if (searchParams.toString()) {
15        url = url + '?' + searchParams.toString();
16      }
17
18      window.gtag('config', process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID!, {
19        page_path: pathname,
20        page_location: url,
21      });
22    }
23  }, [pathname, searchParams]);
24}

Puis utilise ce hook dans un composant client :

1// components/PageTracker.tsx
2'use client';
3
4import { usePageTracking } from '@/hooks/usePageTracking';
5
6export default function PageTracker() {
7  usePageTracking();
8  return null;
9}
10
11// app/layout.tsx
12import PageTracker from '@/components/PageTracker';
13
14export default function RootLayout({ children }: { children: React.ReactNode }) {
15  return (
16    <html lang="pl">
17      <body>
18        {process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID && (
19          <>
20            <GoogleAnalytics GA_MEASUREMENT_ID={process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID} />
21            <PageTracker />
22          </>
23        )}
24        {children}
25      </body>
26    </html>
27  );
28}

4. Suivre des événements personnalisés

Crée un outil pour suivre des événements personnalisés :

1// lib/analytics.ts
2'use client';
3
4export type EventOptions = {
5  category: string;
6  label?: string;
7  value?: number;
8  nonInteraction?: boolean;
9  [key: string]: any;
10};
11
12export const trackEvent = (action: string, options: EventOptions) => {
13  const { category, label, value, nonInteraction = false, ...restOptions } = options;
14
15  if (typeof window !== 'undefined' && window.gtag) {
16    window.gtag('event', action, {
17      event_category: category,
18      event_label: label,
19      value: value,
20      non_interaction: nonInteraction,
21      ...restOptions,
22    });
23  }
24};

Utilisation dans un composant :

1// components/SubscribeButton.tsx
2'use client';
3
4import { trackEvent } from '@/lib/analytics';
5
6export default function SubscribeButton() {
7  const handleSubscribe = () => {
8    // Logique d'abonnement...
9
10    // Suivi de l'événement
11    trackEvent('subscribe', {
12      category: 'Engagement',
13      label: 'Newsletter',
14      value: 1,
15    });
16  };
17
18  return (
19    <button
20      onClick={handleSubscribe}
21      className="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded"
22    >
23      Subscribe to newsletter
24    </button>
25  );
26}

Intégration avec Vercel Analytics

Si tu héberges ton application Next.js sur Vercel, tu peux profiter de l'analytics intégré de Vercel, qui offre un déploiement facile et de solides métriques de performance.

1. Installer le paquet @vercel/analytics

1npm install @vercel/analytics
2# ou
3yarn add @vercel/analytics
4# ou
5pnpm add @vercel/analytics

2. Ajouter le provider d'analytics

1// app/layout.tsx
2import { Analytics } from '@vercel/analytics/react';
3
4export default function RootLayout({
5  children,
6}: {
7  children: React.ReactNode;
8}) {
9  return (
10    <html lang="pl">
11      <body>
12        {children}
13        <Analytics />
14      </body>
15    </html>
16  );
17}

C'est tout ! Vercel collecte automatiquement les données sur les visites de pages et les métriques clés des Web Vitals.

3. Suivre des événements personnalisés dans Vercel Analytics

1// components/ContactForm.tsx
2'use client';
3
4import { track } from '@vercel/analytics';
5import { useState } from 'react';
6
7export default function ContactForm() {
8  const [email, setEmail] = useState('');
9
10  const handleSubmit = (e: React.FormEvent) => {
11    e.preventDefault();
12    // Logique du formulaire de contact...
13
14    // Suivi de l'événement
15    track('contact_form_submitted', { email_domain: email.split('@')[1] });
16  };
17
18  return (
19    <form onSubmit={handleSubmit} className="space-y-4">
20      <div>
21        <label htmlFor="email" className="block mb-1">Email</label>
22        <input
23          id="email"
24          type="email"
25          value={email}
26          onChange={(e) => setEmail(e.target.value)}
27          required
28          className="w-full px-3 py-2 border rounded"
29        />
30      </div>
31      <button
32        type="submit"
33        className="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded"
34      >
35        Send
36      </button>
37    </form>
38  );
39}

Intégrer des outils d'analytics respectueux de la vie privée

Avec la prise de conscience croissante de la vie privée et des réglementations comme le RGPD, beaucoup d'entreprises choisissent des outils d'analytics respectueux de la vie privée. Tu trouveras ci-dessous un exemple d'intégration avec Plausible Analytics.

Intégration avec Plausible Analytics

1// components/PlausibleAnalytics.tsx
2'use client';
3
4import Script from 'next/script';
5
6export default function PlausibleAnalytics({ domain }: { domain: string }) {
7  return (
8    <Script
9      strategy="afterInteractive"
10      data-domain={domain}
11      src="https://plausible.io/js/script.js"
12    />
13  );
14}
15
16// app/layout.tsx
17import PlausibleAnalytics from '@/components/PlausibleAnalytics';
18
19export default function RootLayout({
20  children,
21}: {
22  children: React.ReactNode;
23}) {
24  return (
25    <html lang="pl">
26      <body>
27        {process.env.NEXT_PUBLIC_ANALYTICS_DOMAIN && (
28          <PlausibleAnalytics domain={process.env.NEXT_PUBLIC_ANALYTICS_DOMAIN} />
29        )}
30        {children}
31      </body>
32    </html>
33  );
34}

Intégration avec PostHog

PostHog est une plateforme d'analytics open-source complète qui combine analytics, enregistrement de sessions, fonctionnalités d'A/B testing et bien plus.

1// app/providers.tsx
2'use client';
3
4import posthog from 'posthog-js';
5import { PostHogProvider } from 'posthog-js/react';
6import { usePathname, useSearchParams } from 'next/navigation';
7import { useEffect } from 'react';
8
9if (typeof window !== 'undefined' && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
10  posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
11    api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://app.posthog.com',
12    // Autres options...
13    capture_pageview: false // Désactive le suivi automatique des vues de page
14  });
15}
16
17export function PostHogPageview(): JSX.Element {
18  const pathname = usePathname();
19  const searchParams = useSearchParams();
20
21  useEffect(() => {
22    if (pathname) {
23      let url = window.origin + pathname;
24      if (searchParams && searchParams.toString()) {
25        url = url + `?${searchParams.toString()}`;
26      }
27      posthog.capture('$pageview', {
28        $current_url: url,
29      });
30    }
31  }, [pathname, searchParams]);
32
33  return <></>;
34}
35
36export function PHProvider({ children }: { children: React.ReactNode }) {
37  return (
38    <PostHogProvider client={posthog}>
39      <PostHogPageview />
40      {children}
41    </PostHogProvider>
42  );
43}
44
45// app/layout.tsx
46import { PHProvider } from './providers';
47
48export default function RootLayout({
49  children,
50}: {
51  children: React.ReactNode;
52}) {
53  return (
54    <html lang="pl">
55      <body>
56        <PHProvider>{children}</PHProvider>
57      </body>
58    </html>
59  );
60}

Gérer le RGPD et le consentement des utilisateurs

Conformément aux réglementations sur la protection des données personnelles, le consentement de l'utilisateur doit être obtenu avant de commencer le suivi. Implémentation d'un simple bandeau cookies :

1// components/CookieConsent.tsx
2'use client';
3
4import { useState, useEffect } from 'react';
5
6export default function CookieConsent() {
7  const [consent, setConsent] = useState<boolean | null>(null);
8
9  useEffect(() => {
10    // Vérifie si l'utilisateur a déjà donné son consentement
11    const storedConsent = localStorage.getItem('cookieConsent');
12    if (storedConsent !== null) {
13      setConsent(storedConsent === 'true');
14    } else {
15      setConsent(false); // Pas de consentement par défaut
16    }
17  }, []);
18
19  const handleAccept = () => {
20    localStorage.setItem('cookieConsent', 'true');
21    setConsent(true);
22
23    // Active l'analytics
24    if (window.gtag) {
25      window.gtag('consent', 'update', {
26        'analytics_storage': 'granted'
27      });
28    }
29  };
30
31  const handleReject = () => {
32    localStorage.setItem('cookieConsent', 'false');
33    setConsent(false);
34
35    // Désactive l'analytics
36    if (window.gtag) {
37      window.gtag('consent', 'update', {
38        'analytics_storage': 'denied'
39      });
40    }
41  };
42
43  if (consent === null || consent === true) {
44    return null; // N'affiche pas le bandeau si le consentement a été donné ou n'a pas encore chargé
45  }
46
47  return (
48    <div className="fixed bottom-0 left-0 right-0 bg-gray-100 p-4 shadow-md z-50">
49      <div className="max-w-6xl mx-auto flex flex-col md:flex-row items-center justify-between">
50        <div className="mb-4 md:mb-0">
51          <p className="text-sm">
52            This page uses cookies to analyze traffic. You can decide whether to accept cookies.
53          </p>
54        </div>
55        <div className="flex space-x-4">
56          <button
57            onClick={handleReject}
58            className="px-4 py-2 text-sm border border-gray-300 rounded hover:bg-gray-200"
59          >
60            Reject
61          </button>
62          <button
63            onClick={handleAccept}
64            className="px-4 py-2 text-sm bg-blue-500 text-white rounded hover:bg-blue-600"
65          >
66            Accepter
67          </button>
68        </div>
69      </div>
70    </div>
71  );
72}
73
74// app/layout.tsx
75import CookieConsent from '@/components/CookieConsent';
76
77export default function RootLayout({
78  children,
79}: {
80  children: React.ReactNode;
81}) {
82  return (
83    <html lang="pl">
84      <body>
85        {children}
86        <CookieConsent />
87      </body>
88    </html>
89  );
90}

Adapter Google Analytics aux exigences du RGPD

Modifie l'initialisation de Google Analytics pour tenir compte du consentement de l'utilisateur :

1// components/GoogleAnalytics.tsx
2'use client';
3
4import Script from 'next/script';
5import { useEffect } from 'react';
6
7export default function GoogleAnalytics({ GA_MEASUREMENT_ID }: { GA_MEASUREMENT_ID: string }) {
8  useEffect(() => {
9    // Vérification du consentement de l'utilisateur
10    const consent = localStorage.getItem('cookieConsent') === 'true';
11
12    // Mise à jour de l'état du consentement dans Google Analytics
13    if (window.gtag) {
14      window.gtag('consent', 'update', {
15        'analytics_storage': consent ? 'granted' : 'denied'
16      });
17    }
18  }, []);
19
20  return (
21    <>
22      <Script
23        strategy="afterInteractive"
24        src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
25      />
26      <Script
27        id="google-analytics"
28        strategy="afterInteractive"
29        dangerouslySetInnerHTML={{
30          __html: `
31            window.dataLayer = window.dataLayer || [];
32            function gtag(){dataLayer.push(arguments);}
33            gtag('js', new Date());
34
35            // Définit le consentement par défaut sur 'denied'
36            gtag('consent', 'default', {
37              'analytics_storage': 'denied'
38            });
39
40            // Configure Google Analytics
41            gtag('config', '${GA_MEASUREMENT_ID}', {
42              page_path: window.location.pathname,
43            });
44          `,
45        }}
46      />
47    </>
48  );
49}

Intégration cross-platform : Web, iOS, Android

Pour les applications qui tournent sur plusieurs plateformes, il vaut la peine d'implémenter un suivi analytics cohérent. Tu peux utiliser des bibliothèques comme Segment, Amplitude ou Firebase Analytics, qui proposent des SDK pour différentes plateformes.

Exemple avec Firebase Analytics

1// lib/analytics.ts
2import { initializeApp } from 'firebase/app';
3import { getAnalytics, logEvent, isSupported } from 'firebase/analytics';
4
5const firebaseConfig = {
6  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
7  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
8  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
9  // Autres configurations...
10};
11
12let analytics: any = null;
13
14export async function initAnalytics() {
15  if (typeof window !== 'undefined' && await isSupported()) {
16    const app = initializeApp(firebaseConfig);
17    analytics = getAnalytics(app);
18    return true;
19  }
20  return false;
21}
22
23export function trackEvent(eventName: string, eventParams?: any) {
24  if (analytics) {
25    logEvent(analytics, eventName, eventParams);
26  }
27}
28
29// components/AnalyticsProvider.tsx
30'use client';
31
32import { useEffect } from 'react';
33import { usePathname, useSearchParams } from 'next/navigation';
34import { initAnalytics, trackEvent } from '@/lib/analytics';
35
36export default function AnalyticsProvider({ children }: { children: React.ReactNode }) {
37  const pathname = usePathname();
38  const searchParams = useSearchParams();
39
40  useEffect(() => {
41    initAnalytics();
42  }, []);
43
44  useEffect(() => {
45    if (pathname) {
46      trackEvent('page_view', {
47        page_path: pathname,
48        page_search: searchParams?.toString(),
49      });
50    }
51  }, [pathname, searchParams]);
52
53  return <>{children}</>;
54}

Mesurer les Web Vitals

Les métriques Web Vitals sont essentielles pour comprendre les performances de ton application. Next.js propose un outil intégré pour reporter les Web Vitals aux systèmes d'analytics :

1// app/layout.tsx
2import { useReportWebVitals } from 'next/web-vitals';
3
4export function WebVitals() {
5  useReportWebVitals((metric) => {
6    const { id, name, label, value } = metric;
7
8    // Reporting vers Google Analytics
9    if (window.gtag) {
10      window.gtag('event', name, {
11        event_category: 'Web Vitals',
12        event_label: label,
13        value: Math.round(name === 'CLS' ? value * 1000 : value),
14        non_interaction: true,
15        metric_id: id,
16      });
17    }
18
19    // Reporting vers la console dans l'environnement de développement
20    if (process.env.NODE_ENV === 'development') {
21      console.log(`Web Vital: ${name}`, metric);
22    }
23  });
24
25  return null;
26}
27
28export default function RootLayout({ children }: { children: React.ReactNode }) {
29  return (
30    <html lang="pl">
31      <body>
32        <WebVitals />
33        {children}
34      </body>
35    </html>
36  );
37}

Dashboards d'analytics personnalisés

Parfois, tu as besoin de visualiser les données analytics directement dans ton application. Tu peux implémenter ton propre dashboard d'analytics en récupérant les données depuis l'API Google Analytics ou un autre outil :

1// app/admin/analytics/page.tsx
2import { google } from 'googleapis';
3import { BarChart, LineChart } from '@/components/Charts';
4
5async function getAnalyticsData() {
6  try {
7    // Initialisation du client Google Analytics
8    const auth = new google.auth.GoogleAuth({
9      keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS,
10      scopes: ['https://www.googleapis.com/auth/analytics.readonly'],
11    });
12
13    const analyticsreporting = google.analyticsreporting({
14      version: 'v4',
15      auth,
16    });
17
18    // Récupération des données analytics
19    const response = await analyticsreporting.reports.batchGet({
20      requestBody: {
21        reportRequests: [
22          {
23            viewId: process.env.GA_VIEW_ID,
24            dateRanges: [{ startDate: '30daysAgo', endDate: 'today' }],
25            metrics: [{ expression: 'ga:sessions' }, { expression: 'ga:pageviews' }],
26            dimensions: [{ name: 'ga:date' }],
27          },
28        ],
29      },
30    });
31
32    // Traitement des données
33    const report = response.data.reports[0];
34    const rows = report.data.rows;
35
36    return rows.map((row) => ({
37      date: row.dimensions[0],
38      sessions: parseInt(row.metrics[0].values[0], 10),
39      pageviews: parseInt(row.metrics[0].values[1], 10),
40    }));
41  } catch (error) {
42    console.error('Error during fetching analytics data:', error);
43    return [];
44  }
45}
46
47export default async function AnalyticsDashboard() {
48  const analyticsData = await getAnalyticsData();
49
50  return (
51    <div className="p-6">
52      <h1 className="text-2xl font-bold mb-6">Panneau d'analytics</h1>
53
54      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
55        <div className="bg-white p-4 rounded shadow">
56          <h2 className="text-lg font-semibold mb-4">Sessions vs. page views</h2>
57          <LineChart
58            data={analyticsData}
59            categories={['sessions', 'pageviews']}
60            xKey="date"
61          />
62        </div>
63
64        <div className="bg-white p-4 rounded shadow">
65          <h2 className="text-lg font-semibold mb-4">Page views by day</h2>
66          <BarChart
67            data={analyticsData}
68            category="pageviews"
69            xKey="date"
70          />
71        </div>
72      </div>
73    </div>
74  );
75}

Tester et déboguer l'analytics

Avant de déployer en production, il vaut la peine de tester l'intégration analytics dans l'environnement de développement :

1// lib/analytics.ts
2export function trackEvent(eventName: string, eventParams: any = {}) {
3  // Dans l'environnement de développement, affiche les événements dans la console
4  if (process.env.NODE_ENV === 'development') {
5    console.log(`Analytics Event: ${eventName}`, eventParams);
6    return;
7  }
8
9  // Dans l'environnement de production, envoie au service d'analytics
10  if (window.gtag) {
11    window.gtag('event', eventName, eventParams);
12  }
13}

Pour les tests, tu peux aussi utiliser des extensions de navigateur comme Google Analytics Debugger, Tag Assistant ou Facebook Pixel Helper.

Bonnes pratiques

  1. Respecte la vie privée des utilisateurs - informe toujours de l'utilisation d'outils d'analytics et recueille le consentement
  2. Minimise l'impact sur les performances - utilise la stratégie afterInteractive ou lazyOnload pour les scripts d'analytics
  3. Segmente les données - catégorise les événements pour faciliter l'analyse des données
  4. Utilise plusieurs sources de données - combine les données de différents outils pour une vision plus complète
  5. Vérifie régulièrement les données - contrôle la justesse des données analytics
  6. Analyse les tendances - cherche des schémas et des anomalies dans les données
  7. Anonymise les données - collecte les données conformément au RGPD

Frameworks d'analytics pour Next.js

Pour des besoins plus avancés, il vaut la peine d'envisager d'utiliser des frameworks d'analytics spécialisés pour Next.js :

  1. Next-GA - intégration simplifiée avec Google Analytics
  2. Use-Analytics - un hook universel pour s'intégrer avec différents fournisseurs d'analytics
  3. React-GA - une bibliothèque populaire pour intégrer React avec Google Analytics
  4. Next-Plausible - intégration avec l'outil respectueux de la vie privée Plausible

Résumé

Le web analytics est un outil clé pour comprendre les utilisateurs et optimiser les applications. Next.js propose de nombreuses façons de s'intégrer avec divers outils d'analytics, de Google Analytics à Vercel Analytics en passant par des alternatives plus respectueuses de la vie privée comme Plausible ou Fathom.

N'oublie pas que le plus important est de ne collecter que les données vraiment nécessaires et de le faire dans le respect de la vie privée des utilisateurs. Les bonnes pratiques incluent l'obtention du consentement, l'anonymisation des données et l'information des utilisateurs sur les données collectées et leur finalité.

Code de cette leçon : App.tsx
1import React, { useState, useEffect } from 'react';
2
3// Demo: Web Analytics Integration
4interface AnalyticsEvent {
5  id: string;
6  type: 'pageview' | 'click' | 'scroll' | 'conversion' | 'error';
7  page: string;
8  timestamp: string;
9  data?: string;
10}
11
12interface AnalyticsMetrics {
13  pageViews: number;
14  uniqueVisitors: number;
15  bounceRate: number;
16  avgSessionDuration: number;
17  topPages: { page: string; views: number }[];
18}
19
20function AnalyticsDemo() {
21  const [events, setEvents] = useState<AnalyticsEvent[]>([]);
22  const [metrics, setMetrics] = useState<AnalyticsMetrics>({
23    pageViews: 1247,
24    uniqueVisitors: 892,
25    bounceRate: 34.2,
26    avgSessionDuration: 245,
27    topPages: [
28      { page: '/', views: 456 },
29      { page: '/blog', views: 312 },
30      { page: '/products', views: 278 },
31      { page: '/about', views: 201 },
32    ],
33  });
34  const [activeProvider, setActiveProvider] = useState<'ga4' | 'plausible' | 'custom'>('ga4');
35  const [isTracking, setIsTracking] = useState(true);
36
37  const providers = [
38    { id: 'ga4' as const, name: 'Google Analytics 4', color: '#f59e0b' },
39    { id: 'plausible' as const, name: 'Plausible', color: '#10b981' },
40    { id: 'custom' as const, name: 'Custom Analytics', color: '#8b5cf6' },
41  ];
42
43  const trackEvent = (type: AnalyticsEvent['type'], page: string, data?: string) => {
44    if (!isTracking) return;
45    const event: AnalyticsEvent = {
46      id: Math.random().toString(36).substr(2, 6),
47      type, page, data,
48      timestamp: new Date().toLocaleTimeString(),
49    };
50    setEvents(prev => [event, ...prev.slice(0, 19)]);
51    if (type === 'pageview') {
52      setMetrics(prev => ({ ...prev, pageViews: prev.pageViews + 1 }));
53    }
54  };
55
56  const eventColors: Record<string, string> = {
57    pageview: '#3b82f6', click: '#10b981', scroll: '#f59e0b', conversion: '#8b5cf6', error: '#ef4444',
58  };
59
60  const codeSnippets: Record<string, string> = {
61    ga4: "// app/layout.tsx\nimport { GoogleAnalytics } from '@next/third-parties/google';\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>{children}</body>\n      <GoogleAnalytics gaId=\"G-XXXXXXX\" />\n    </html>\n  );\n}",
62    plausible: "// app/layout.tsx\nimport Script from 'next/script';\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Script\n          defer\n          data-domain=\"yourdomain.com\"\n          src=\"https://plausible.io/js/script.js\"\n        />\n      </body>\n    </html>\n  );\n}",
63    custom: "// lib/analytics.ts\nexport function trackPageView(url: string) {\n  fetch('/api/analytics', {\n    method: 'POST',\n    body: JSON.stringify({ type: 'pageview', url, timestamp: Date.now() }),\n  });\n}\n\n// app/layout.tsx - use in useEffect\nuseEffect(() => {\n  trackPageView(pathname);\n}, [pathname]);",
64  };
65
66  return (
67    <div style={{ background: '#0f0f23', minHeight: '100vh', padding: '20px', color: '#e0e0e0', fontFamily: 'system-ui' }}>
68      <h1 style={{ color: '#64ffda', marginBottom: '8px' }}>Web Analytics Dashboard</h1>
69      <p style={{ color: '#8892b0', marginBottom: '24px' }}>Intégration de l'analytics dans Next.js</p>
70
71      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '16px', marginBottom: '24px' }}>
72        {[
73          { label: 'Page Views', value: metrics.pageViews.toLocaleString(), color: '#3b82f6' },
74          { label: 'Unique Visitors', value: metrics.uniqueVisitors.toLocaleString(), color: '#10b981' },
75          { label: 'Bounce Rate', value: metrics.bounceRate + '%', color: '#f59e0b' },
76          { label: 'Avg Session', value: Math.floor(metrics.avgSessionDuration / 60) + 'm ' + (metrics.avgSessionDuration % 60) + 's', color: '#8b5cf6' },
77        ].map(m => (
78          <div key={m.label} style={{ background: '#1a1a2e', padding: '16px', borderRadius: '12px', border: '1px solid #333', textAlign: 'center' }}>
79            <div style={{ fontSize: '28px', fontWeight: 'bold', color: m.color }}>{m.value}</div>
80            <div style={{ fontSize: '13px', color: '#8892b0', marginTop: '4px' }}>{m.label}</div>
81          </div>
82        ))}
83      </div>
84
85      <div style={{ display: 'flex', gap: '8px', marginBottom: '20px' }}>
86        {providers.map(p => (
87          <button key={p.id} onClick={() => setActiveProvider(p.id)} style={{
88            padding: '8px 16px', borderRadius: '8px', border: 'none', cursor: 'pointer',
89            background: activeProvider === p.id ? p.color : '#1a1a2e', color: activeProvider === p.id ? '#fff' : '#8892b0', fontWeight: 'bold',
90          }}>{p.name}</button>
91        ))}
92        <button onClick={() => setIsTracking(!isTracking)} style={{ padding: '8px 16px', borderRadius: '8px', border: 'none', cursor: 'pointer', background: isTracking ? '#10b981' : '#ef4444', color: '#fff', marginLeft: 'auto' }}>
93          {isTracking ? 'Tracking ON' : 'Tracking OFF'}
94        </button>
95      </div>
96
97      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px', marginBottom: '24px' }}>
98        <div>
99          <h3 style={{ color: '#64ffda', fontSize: '16px' }}>Simuler des événements</h3>
100          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>
101            {[
102              { type: 'pageview' as const, label: 'Page View', page: '/home' },
103              { type: 'click' as const, label: 'Button Click', page: '/cta' },
104              { type: 'scroll' as const, label: 'Scroll 50%', page: '/blog' },
105              { type: 'conversion' as const, label: 'Conversion', page: '/checkout' },
106              { type: 'error' as const, label: 'JS Error', page: '/app' },
107            ].map(e => (
108              <button key={e.type} onClick={() => trackEvent(e.type, e.page, e.label)} style={{
109                padding: '10px', borderRadius: '8px', border: '1px solid ' + eventColors[e.type], background: eventColors[e.type] + '20', color: eventColors[e.type], cursor: 'pointer',
110              }}>{e.label}</button>
111            ))}
112          </div>
113        </div>
114        <div>
115          <h3 style={{ color: '#64ffda', fontSize: '16px' }}>Event Log</h3>
116          <div style={{ maxHeight: '200px', overflow: 'auto', background: '#1a1a2e', borderRadius: '8px', padding: '12px', border: '1px solid #333' }}>
117            {events.map(e => (
118              <div key={e.id} style={{ fontSize: '12px', padding: '4px 0', borderBottom: '1px solid #222', display: 'flex', gap: '8px' }}>
119                <span style={{ color: eventColors[e.type], minWidth: '80px' }}>{e.type}</span>
120                <span style={{ color: '#64ffda' }}>{e.page}</span>
121                <span style={{ color: '#555', marginLeft: 'auto' }}>{e.timestamp}</span>
122              </div>
123            ))}
124            {events.length === 0 && <p style={{ color: '#555', margin: 0 }}>Aucun événement</p>}
125          </div>
126        </div>
127      </div>
128
129      <div style={{ background: '#1a1a2e', padding: '16px', borderRadius: '8px', border: '1px solid #333' }}>
130        <h3 style={{ color: '#f59e0b', fontSize: '14px', marginTop: 0 }}>Code d'intégration ({providers.find(p => p.id === activeProvider)?.name}) :</h3>
131        <pre style={{ fontSize: '12px', color: '#e0e0e0', margin: 0, overflow: 'auto', whiteSpace: 'pre-wrap' }}>{codeSnippets[activeProvider]}</pre>
132      </div>
133    </div>
134  );
135}
136
137export default AnalyticsDemo;

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Les Core Web Vitals comprennent :

Tâches pratiques dans le jeu

  • Éditeur de code

    Crée un panneau de contrôle avec monitoring des Web Vitals

  • Clique dans l'ordre

    Place la syntaxe de la fonction reportWebVitals dans Next.js

  • Éditeur de code

    Implémente un dashboard optimisé pour les performances

Articles utiles