> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scanova.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration SPA et frameworks

> Installez le SDK navigateur Scanova dans React, Next.js, Vue et autres SPA : évitez la double initialisation et suivez les pages vues aux changements de route.

Les applications monopage (SPA) ont un cycle de vie différent des sites multipages classiques. Le navigateur ne recharge pas entièrement lors de la navigation, si bien que le `autoPageview` standard ne se déclenche qu'une fois, au chargement initial. Ce guide montre comment gérer correctement le suivi dans React, Next.js et Vue.

## La différence essentielle avec les SPA

Sur un site classique, chaque navigation provoque un rechargement complet qui réexécute le SDK et déclenche un `pageview`. Dans une SPA :

* La page se charge **une seule fois**
* Les navigations suivantes sont pilotées par JavaScript (l'URL change, mais la page ne se recharge pas)
* `autoPageview` ne se déclenche qu'à ce premier chargement
* **Vous devez déclencher manuellement un événement `pageview` à chaque changement de route**

## Règles générales pour toutes les SPA

1. **Chargez le snippet du SDK une seule fois** — dans votre fichier HTML racine ou votre composant racine, pas dans chaque composant de page ou de route
2. **Appelez `scanova('init', ...)` une seule fois** — dans le point d'entrée de votre application
3. **Désactivez `autoPageview`** — gérez vous-même les pages vues lors des changements de route
4. **Déclenchez `scanova('track', 'pageview')` à chaque changement de route** — via l'événement de navigation ou le hook de votre routeur

***

## React (Create React App / Vite)

**`public/index.html`** — charger le SDK :

```html theme={null}
<head>
  <script>
  (function(w,d,s,o,f,js,fjs){
    w['ScanovaTrackingObject']=o;w[o]=w[o]||function(){(w[o].q=w[o].q||[]).push(arguments)};
    js=d.createElement(s),fjs=d.getElementsByTagName(s)[0];
    js.id=o;js.src=f;js.async=1;fjs.parentNode.insertBefore(js,fjs);
  })(window,document,'script','scanova','https://cdn.scanova.io/ct/js/qcg.min.js');
  </script>
</head>
```

**`src/main.jsx`** ou **`src/App.jsx`** — initialiser une fois et suivre les changements de route :

```jsx theme={null}
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

// Init once at app boot (outside any component)
window.scanova?.('init', 'YOUR_SITE_ID', {
  autoPageview: false,   // manage manually
  autoClicks: true,
  autoForms: true,
  autoScroll: true,
});

// Hook to track route changes
function usePageTracking() {
  const location = useLocation();

  useEffect(() => {
    window.scanova?.('track', 'pageview', {
      path: location.pathname,
    });
  }, [location.pathname]);
}

export default function App() {
  usePageTracking();
  return <RouterOutlet />;
}
```

***

## Next.js (App Router)

Dans l'App Router de Next.js, utilisez un composant client dans votre layout racine.

**`app/layout.tsx`:**

```tsx theme={null}
import ScanovaTracker from '@/components/ScanovaTracker';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <head>
        <script
          dangerouslySetInnerHTML={{
            __html: `
              (function(w,d,s,o,f,js,fjs){
                w['ScanovaTrackingObject']=o;w[o]=w[o]||function(){(w[o].q=w[o].q||[]).push(arguments)};
                js=d.createElement(s),fjs=d.getElementsByTagName(s)[0];
                js.id=o;js.src=f;js.async=1;fjs.parentNode.insertBefore(js,fjs);
              })(window,document,'script','scanova','https://cdn.scanova.io/ct/js/qcg.min.js');
              scanova('init', 'YOUR_SITE_ID', { autoPageview: false, autoClicks: true, autoForms: true, autoScroll: true });
            `,
          }}
        />
      </head>
      <body>
        <ScanovaTracker />
        {children}
      </body>
    </html>
  );
}
```

**`components/ScanovaTracker.tsx`:**

```tsx theme={null}
'use client';

import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect } from 'react';

export default function ScanovaTracker() {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    window.scanova?.('track', 'pageview', {
      path: pathname,
    });
  }, [pathname, searchParams]);

  return null;
}
```

***

## Next.js (Pages Router)

**`pages/_app.tsx`:**

```tsx theme={null}
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter();

  useEffect(() => {
    // Init once
    (window as any).scanova?.('init', 'YOUR_SITE_ID', {
      autoPageview: false,
      autoClicks: true,
      autoForms: true,
      autoScroll: true,
    });

    // Track initial page
    (window as any).scanova?.('track', 'pageview', { path: router.pathname });

    // Track subsequent navigations
    const handleRouteChange = (url: string) => {
      (window as any).scanova?.('track', 'pageview', { path: url });
    };

    router.events.on('routeChangeComplete', handleRouteChange);
    return () => router.events.off('routeChangeComplete', handleRouteChange);
  }, []);

  return <Component {...pageProps} />;
}
```

Ajoutez le chargeur du SDK dans **`pages/_document.tsx`** :

```tsx theme={null}
import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html>
      <Head>
        <script dangerouslySetInnerHTML={{ __html: `
          (function(w,d,s,o,f,js,fjs){
            w['ScanovaTrackingObject']=o;w[o]=w[o]||function(){(w[o].q=w[o].q||[]).push(arguments)};
            js=d.createElement(s),fjs=d.getElementsByTagName(s)[0];
            js.id=o;js.src=f;js.async=1;fjs.parentNode.insertBefore(js,fjs);
          })(window,document,'script','scanova','https://cdn.scanova.io/ct/js/qcg.min.js');
        `}} />
      </Head>
      <body><Main /><NextScript /></body>
    </Html>
  );
}
```

***

## Vue 3 (Vue Router)

**`index.html`** — charger le SDK :

```html theme={null}
<head>
  <script>
  (function(w,d,s,o,f,js,fjs){
    w['ScanovaTrackingObject']=o;w[o]=w[o]||function(){(w[o].q=w[o].q||[]).push(arguments)};
    js=d.createElement(s),fjs=d.getElementsByTagName(s)[0];
    js.id=o;js.src=f;js.async=1;fjs.parentNode.insertBefore(js,fjs);
  })(window,document,'script','scanova','https://cdn.scanova.io/ct/js/qcg.min.js');
  </script>
</head>
```

**`src/main.ts`:**

```typescript theme={null}
import { createApp } from 'vue';
import { createRouter } from 'vue-router';
import App from './App.vue';

const router = createRouter({ ... });

// Init once
window.scanova?.('init', 'YOUR_SITE_ID', {
  autoPageview: false,
  autoClicks: true,
  autoForms: true,
  autoScroll: true,
});

// Track route changes
router.afterEach((to) => {
  window.scanova?.('track', 'pageview', { path: to.path });
});

createApp(App).use(router).mount('#app');
```

***

## Erreurs fréquentes

| Erreur                                                                       | Conséquence                                                                                                                                    | Correctif                                                                                                                                       |
| ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Appeler `scanova('init', ...)` dans chaque composant de page ou de route     | Appels d'init multiples → écouteurs de suivi dupliqués → événements en double                                                                  | N'appelez `init` qu'une fois, à la racine de l'application                                                                                      |
| Utiliser `autoPageview: true` dans une SPA                                   | Ne se déclenche qu'au premier chargement et manque toutes les navigations                                                                      | Désactivez-le et suivez manuellement à chaque changement de route                                                                               |
| Ajouter le script du SDK à chaque composant de page                          | Le SDK se charge plusieurs fois                                                                                                                | Chargez-le une fois depuis le HTML ou le layout racine                                                                                          |
| Appeler `track` sans attendre le chargement du SDK                           | Échecs silencieux                                                                                                                              | Utilisez le chaînage optionnel `window.scanova?.()`, ou le motif de file (les appels antérieurs au chargement sont mis en file automatiquement) |
| Utiliser `autoScroll: true` en attendant des paliers de défilement par route | Les paliers ne sont pas réinitialisés au changement de route : une personne arrivée à 75 % sur `/home` ne les redéclenchera pas sur `/pricing` | Désactivez `autoScroll` dans les SPA et envoyez les événements `scroll` manuellement si la profondeur par route compte                          |
