> ## 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.

# SPA- und Framework-Einrichtung

> Das Scanova-Browser-SDK in React, Next.js, Vue und anderen Single-Page-Frameworks einbinden, doppelte Initialisierung vermeiden und Routenwechsel erfassen.

Single-Page-Apps (SPAs) haben einen anderen Seitenlebenszyklus als klassische mehrseitige Websites. Der Browser lädt bei der Navigation nicht vollständig neu, sodass das übliche `autoPageview` nur einmal auslöst — beim ersten Laden. Dieser Leitfaden zeigt, wie Sie das Tracking in React, Next.js und Vue richtig umsetzen.

## Der entscheidende Unterschied bei SPAs

Auf einer klassischen Website löst jede Navigation einen vollständigen Neuaufbau aus, der das SDK erneut ausführt und ein `pageview` sendet. In einer SPA:

* Die Seite lädt **einmal**
* Alle weiteren Navigationen laufen über JavaScript (die URL ändert sich, die Seite lädt nicht neu)
* `autoPageview` löst nur bei diesem ersten Laden aus
* **Sie müssen bei jedem Routenwechsel selbst ein `pageview`-Ereignis auslösen**

## Allgemeine Regeln für alle SPAs

1. **Laden Sie das SDK-Snippet einmal** — in Ihrer Root-HTML-Datei oder Root-Komponente, nicht in jeder Seiten- oder Routen-Komponente
2. **Rufen Sie `scanova('init', ...)` einmal auf** — im Einstiegspunkt Ihrer Anwendung
3. **Deaktivieren Sie `autoPageview`** — steuern Sie Seitenaufruf-Ereignisse bei Routenwechseln selbst
4. **Lösen Sie bei jedem Routenwechsel `scanova('track', 'pageview')` aus** — über das Navigationsereignis oder den Hook Ihres Routers

***

## React (Create React App / Vite)

**`public/index.html`** — SDK laden:

```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`** oder **`src/App.jsx`** — einmal initialisieren und Routenwechsel erfassen:

```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)

Verwenden Sie im App Router von Next.js eine Client-Komponente in Ihrem Root-Layout.

**`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} />;
}
```

Fügen Sie den SDK-Loader in **`pages/_document.tsx`** ein:

```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`** — SDK laden:

```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');
```

***

## Häufige Fehler

| Fehler                                                                  | Folge                                                                                                                                            | Lösung                                                                                                                            |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| `scanova('init', ...)` in jeder Seiten- oder Routen-Komponente aufrufen | Mehrfache init-Aufrufe → doppelte Auto-Tracking-Listener → doppelte Ereignisse                                                                   | `init` nur einmal im Anwendungs-Root aufrufen                                                                                     |
| `autoPageview: true` in einer SPA verwenden                             | Löst nur beim ersten Laden aus, verpasst alle Navigationen                                                                                       | Deaktivieren und bei Routenwechseln manuell erfassen                                                                              |
| Das SDK-Skript in jede Seiten-Komponente einfügen                       | Das SDK wird mehrfach geladen                                                                                                                    | Einmal aus dem Root-HTML oder Root-Layout laden                                                                                   |
| `track` aufrufen, bevor das SDK geladen ist                             | Stille Fehlschläge                                                                                                                               | `window.scanova?.()` mit Optional Chaining verwenden, oder das Queue-Muster (Aufrufe vor dem Laden werden automatisch eingereiht) |
| `autoScroll: true` verwenden und Scroll-Marken pro Route erwarten       | Marken werden bei Routenwechseln nicht zurückgesetzt — wer auf `/home` bis 75 % gescrollt hat, löst diese Marken auf `/pricing` nicht erneut aus | `autoScroll` in SPAs deaktivieren und `scroll`-Ereignisse manuell senden, wenn Scrolltiefe pro Route zählt                        |
