RenderContextDocumentation

SPAs & cross-domain linking

Use this guide when your product spans multiple domains (for example a marketing site and a signed-in app) or when you use client-side routing (React Router, Next.js, and similar).

How linking works

Configure allowed domains for your environment in the console. After the SDK loads, outbound links to another allowed domain receive a short-lived _rcquery parameter with the visitor's viewerId and sessionId. On arrival, the destination SDK reads and strips _rc, persists the ids, and calls renderContext so experiments and flags stay consistent.

The link param expires after 120 seconds. Exposure and context tokens are not passed in links — the destination re-mints them via its own renderContext call.

The one rule for single-page apps

Load rendercontext.min.js before your app bundle runs. If your framework boots client-side routing first, an auth redirect can drop _rc from the URL before the SDK reads it — and the destination site will mint a new visitor id.

Put the install snippet in index.html before your application module — not from useEffect.

React hooks

After loading the browser SDK with the Vite plugin, a Next.js script, or the plain snippet, install the React helpers to read values inside components.

pnpm add @rendercontext/sdk react

import { useIsEnabled } from "@rendercontext/sdk/react";

function Checkout() {
  const enabled = useIsEnabled("new_checkout", false);
  return enabled ? <NewCheckout /> : <LegacyCheckout />;
}

The hooks wait for the browser SDK and return a safe default while it loads. Use useFlag for string values and useVariant for experiments.

Vite / React (SPA)

Use the @rendercontext/sdk/vite plugin to inject the install snippet before your <script type="module"> entry. Deferred scripts run in document order — RenderContext loads first.

pnpm add -D @rendercontext/sdk vite
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { renderContext } from "@rendercontext/sdk/vite";

export default defineConfig({
  plugins: [
    renderContext({ projectKey: process.env.VITE_ANALYTICS_PROJECT_KEY }),
    react(),
  ],
});

Or put the script in index.html manually before your module entry:

<script
  defer
  src="https://cdn.rendercontext.com/rendercontext.min.js"
  data-project="pk_live_YOUR_KEY"
></script>
<script type="module" src="/src/main.tsx"></script>

Next.js

Use @rendercontext/sdk/next in your root layout or pages/_document.tsx. Do not inject the script from useEffect — client routing can run first.

pnpm add @rendercontext/sdk next
// app/layout.tsx
import { RenderContextScript } from "@rendercontext/sdk/next";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <RenderContextScript
          projectKey={process.env.NEXT_PUBLIC_ANALYTICS_PROJECT_KEY}
        />
        {children}
      </body>
    </html>
  );
}

Pages Router: put the component in <Head> inside pages/_document.tsx. Cross-domain linking passes visitor ids in the URL query string — if the router runs before the SDK loads, those params can be overwritten.

Auth and viewer context

Cross-domain linking only transfers viewer and session ids. If you use signed-in flag targeting, wire viewer context after auth resolves — not while auth is still loading.

  • Loading: do nothing
  • Signed in: await renderContext.setViewerContext(...)
  • Signed out (confirmed): clearViewerContext() only if you previously set viewer context

See Signing tokens for attribute-based targeting.

Anti-patterns

  • Injecting rendercontext.min.js from useEffect when the URL might change before the SDK runs
  • Client-side redirects that drop the query string before the SDK loads (unless the script already ran)
  • Calling clearViewerContext() on every unsigned-in page load
  • Calling clearViewerContext() while auth is still restoring session state

Verify it works

  1. Open the marketing site, wait for renderContext in the Network tab.
  2. Click a link to the app domain. The arrival URL should briefly include _rc (it may be stripped immediately after load).
  3. On the app domain, confirm renderContext sends the same viewerId as the marketing site (check the request body or localStorage keys rc_hashed_viewer_id / rc_session_id).
Back to docs