Denne oversættelse er maskinoversat og afventer gennemgang.Skift til engelsk
På denne side

Single-page apps, frameworks and tag managers

A single-page app changes the URL without loading a new document, a framework re-renders its own markup, and a tag manager injects scripts on its own schedule. This page shows what the collector does in those setups and what you do to keep one clean install.

What it is

Availability: Evaluation access. Availability depends on your plan, role and the modules enabled for your organisation.

The collector is a plain script. It needs one core tag per document, a Site Key that matches the page origin, and a window and document to run in. It does not depend on your framework. The core script watches client-side navigation through the History API when a protected action matches a page path, and it does nothing else on a route change. The page check belongs to the document, not to the route.

The typed @noxtica/sdk wrapper has React, Next.js and Vue helpers in its source. That package is not installable from the public registry today (the registry answered E404 on 2026-10-27), so this page gives no install command for it. Ask your account team for access; see SDK family and availability. Every recipe below works with the script tag alone.

Prerequisites

  • A Site Key for the origin your app is served from. Staging and production origins have separate keys. A key used on another origin gets 403 with Origin mismatch.
  • The core tag from Backoffice → Integrations & API → Script & Consent → Snippets. See Install and Browser Runtime.
  • Your consent mode decided. See Consent modes.
  • For tag managers: the right to publish tags that run on every page of the site.

Steps

1. Load the core once, in the app shell

Put the tag in the document that the app mounts into: index.html, the root layout, or the theme’s <head>. Do not add it per route or per component.

<head>
	<script>
		document.addEventListener('noxtica:collected', (event) => {
			if (event.detail.skipped === 'consent_required') return; // not an error
			// Use event.detail.fingerprintId as a hint for your backend.
		});
	</script>
	<script
		src="https://collect.noxtica.com/collector/noxtica-collector-core.js"
		data-site-key="pk_prod_your_site_key_here"
		data-auto-init
		data-auto-check-once
		async
	></script>
</head>

Register your noxtica:* listeners in a script that runs before the tag. An event that already fired is not replayed. If your framework cannot run code before the tag, queue calls with the window.noxtica stub instead, and read window.noxticaClient and window.noxticaResult after load. See Client API, events and the command queue.

If the core competes with the initial render or hydration, inject it after the load event and the next idle period. A ready-made snippet is in Load timing and weight.

2. Pick a framework recipe

SetupWhat you do
Vite, Create React App or any bundler with an index.htmlAdd the tag from step 1 to index.html.
VueAdd the tag to index.html.
Next.js, App RouterRender the tag with next/script in the root layout.
AngularAdd the tag to index.html. To keep the collector’s timers out of change detection, load it outside the Angular zone; see Single-page apps and frameworks.
Server-side renderingEmit the tag in the HTML. The loader does nothing without a document, so a server render is not affected. Do not import or call the collector in server code.

For Next.js, the markup below is what the wrapper’s NoxticaScript component renders, taken from the wrapper source. afterInteractive is its default strategy. Test it in your own app:

// app/layout.tsx
import Script from 'next/script';

export default function RootLayout({ children }: { children: React.ReactNode }) {
	return (
		<html lang="en">
			<body>
				{children}
				<Script
					src="https://collect.noxtica.com/collector/noxtica-collector-core.js"
					strategy="afterInteractive"
					data-site-key="pk_prod_your_site_key_here"
					data-auto-init=""
					data-auto-check-once=""
				/>
			</body>
		</html>
	);
}

The wrapper’s helpers, for accounts that have the package:

HelperWhat it does, from the 0.3.0 source
NoxticaProvider, useNoxtica (React)The provider creates one client in an effect, so it runs in the browser and not on the server. useNoxtica() returns { client, loading, error }. The provider destroys its client when it unmounts.
useVerdict (React)Requests a verdict when the client is ready, unless you pass auto: false. Returns { verdict, loading, error, refresh }.
NoxticaScript (Next.js)Renders the tag through next/script with data-site-key, data-auto-init and data-auto-check-once. Options: apiUrl, ingestUrl, scriptUrl, script, extensions, auto, checkIntervalDays, debug, logLevel, strategy. It has no consent-mode option.
createNoxtica, useNoxtica (Vue 3)A plugin and a composable. They return client, loading, error, verdict, getVerdict and identify. The plugin stays inert during server rendering.

The wrapper’s init() rejects outside a browser. Guard shared code with isBrowser().

3. Handle route changes

A route change does not run a new page check. data-auto-check-once runs checkOnce() once for the document, and the cache answers later calls. What you can rely on:

  • Protected actions that match a Page path run on the initial load and again when the History API changes the URL (pushState, replaceState, popstate). The core shares one wrapper per page and restores the original methods when no subscriber is left. It leaves your own wrappers in place.
  • Calling checkOnce() on every route is allowed. With the default recordVisit: true, a cache hit records a visit with the collector, so each call sends a request. Pass { recordVisit: false } when you do not want that.
  • Call checkOnce({ forceRefresh: true }) or collectAndSubmit() when a flow needs a fresh check, and not otherwise. Both collect again and submit.
  • For application events and session tags, use track, event and setTag from Custom events, session tags and request tracing.

Do not decide whether Noxtica is on a page from an attribute that your framework renders. A locale switch or a remount can clear attributes on <html>. Decide from the host name or your build configuration.

4. Protect forms that your script submits

A script-submitted form (React, Vue, any fetch flow) cannot receive the hidden nox_request_id field. Read form.dataset.noxRequestId at the top of your submit handler, and call protect synchronously when the id is not there yet. The full handler is in Protected actions. The id is withdrawn when your handler returns, so a retry runs a fresh check.

5. Install through a tag manager or a CMS theme

A tag manager or a theme has to put the same script element on the page, and nothing more. The collector sees the element, not the tool. Check these points in your tool:

  1. Raw HTML. Use the tag type that accepts a <script> with attributes, or a script that creates the element. The attributes data-site-key and data-auto-init must be present when the element is added. The loader reads them once.
  2. File name. Keep noxtica-collector in the src file name. Do not rename the file.
  3. One tag. Remove a hand-pasted tag when the tag manager adds one. A second core tag is ignored and logs NX001.
  4. Order. Define the window.noxtica stub and any listeners before the core runs. If the tool cannot order them, read window.noxticaClient after load.
  5. Trigger. Fire the tag on every page of the site. For an api consent mode, also fire a call to window.noxticaClient.setConsent(...) from the trigger that carries your consent decision, after window.noxticaClient exists.
  6. Dynamic injection. A tag added after the page has loaded works. Set every attribute before you append the element, because the loader reads them when it starts.
  7. Google Consent Mode. When Google tags run on the page, the bridge can follow analytics_storage. See Consent modes.

For a CMS theme, add the tag to the template that outputs <head>, or to the theme’s header-scripts setting. Tag managers and themes differ in how they order and defer scripts; Noxtica does not publish tool-specific steps.

// Custom HTML or script-injection example: set the attributes, then append the element.
if (!window.noxtica || !window.noxtica.v) {
	const s = document.createElement('script');
	s.src = 'https://collect.noxtica.com/collector/noxtica-collector-core.js';
	s.async = true;
	s.setAttribute('data-site-key', 'pk_prod_your_site_key_here');
	s.setAttribute('data-auto-init', '');
	s.setAttribute('data-auto-check-once', '');
	document.head.appendChild(s);
}

The window.noxtica.v check skips the injection when a core script has already claimed the page.

6. Check script optimizers and duplicate tags

A script optimizer that rewrites or defers script tags can delay the collector. The Backoffice notes that Cloudflare Rocket Loader defers every script tag it sees unless the tag carries data-cfasync="false". The session-replay early loader requires that attribute; see Replay recorder head. Noxtica publishes no such requirement for the core tag. If an optimizer delays your core tag, test the tag with data-cfasync="false".

A framework that mounts an effect twice, such as React strict mode in development, can add the tag twice. Guard the injection as in step 5. A duplicate is harmless: the second core script logs NX001 and stops without creating a client.

Verify it works

  1. Reload a page, then navigate to another route without a reload. The route change itself adds no POST /client/submit request, unless a Page path protected action matches the new route.
  2. typeof window.noxticaClient is 'object', and window.noxtica.v holds the loader version.
  3. document.querySelectorAll('script[src*="noxtica-collector"]').length is 1.
  4. If a Page path protected action matches a route, navigating to it starts a protected-action check. Calling await window.noxticaClient.protect('<name>') resolves a requestId, and a bound form receives a noxtica:protected event.
  5. Your listener received noxtica:collected once for the document.

Troubleshooting

SymptomCauseFix
NX001: already loaded in the consoleTwo core tags: for example a hand-pasted one and a tag-manager oneRemove one. The initial tag keeps running.
window.noxticaClient is undefined with a tag managerThe injected element lacks data-auto-init or data-site-key, or the tag manager changed the file nameSet both attributes before appending, and keep noxtica-collector in the file name.
403 with Origin mismatch on stagingThe staging origin uses the production Site KeyCreate a domain for the staging origin and use its key.
A noxtica:collected listener never firesThe listener ran after the event, or the tag was injected lateRegister listeners before the tag, or read window.noxticaResult.
Route changes send a request each timecheckOnce() is called per route with recordVisit left at trueCall it once, or pass { recordVisit: false }.
The id is missing in a framework form handlerThe handler read it after an awaitRead form.dataset.noxRequestId and call protect before any await.
window.noxticaClient appears lateAn optimizer defers the scriptTest data-cfasync="false", or inject the tag after load on purpose.
@noxtica/sdk import failsThe package is not on the public registryUse the script tag, or ask your account team for access.