Developer pages

Remix

Install HumanBehavior in Remix / React Router 7 using entry.client.tsx and a safe window.ENV handoff.

Remix keeps server and browser entries separate. Initialize HumanBehavior in entry.client.tsx, and explicitly hand public env values from the server document to window.ENV.

Wizard detection

The wizard detects Remix when it sees either:

SignalMeaning
@remix-run/reactClassic Remix app
@react-router/devReact Router 7 / Remix successor

This detection runs before generic React so Remix apps do not get the Vite React provider path.

Package

pnpm add humanbehavior-js

Environment variables

HUMANBEHAVIOR_API_KEY=your-api-key
HUMANBEHAVIOR_INGESTION_URL=https://ingest.humanbehavior.co

Remix does not automatically expose env vars to browser bundles. The reference pattern uses window.ENV.

Expose public values from app/root.tsx

The exact file shape depends on your Remix version, but the document should serialize only the values the browser needs:

export function loader() {
  return {
    ENV: {
      HUMANBEHAVIOR_API_KEY: process.env.HUMANBEHAVIOR_API_KEY,
      HUMANBEHAVIOR_INGESTION_URL: process.env.HUMANBEHAVIOR_INGESTION_URL,
    },
  };
}

export default function App() {
  const data = useLoaderData<typeof loader>();

  return (
    <html>
      <head />
      <body>
        <Outlet />
        <script
          dangerouslySetInnerHTML={{
            __html: `window.ENV = ${JSON.stringify(data.ENV)}`,
          }}
        />
        <Scripts />
      </body>
    </html>
  );
}

Keep private secrets out of window.ENV.

Init in app/entry.client.tsx

import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { RemixBrowser } from "@remix-run/react";
import { HumanBehaviorTracker } from "humanbehavior-js";

declare global {
  interface Window {
    ENV?: {
      HUMANBEHAVIOR_API_KEY?: string;
      HUMANBEHAVIOR_INGESTION_URL?: string;
    };
  }
}

const apiKey = window.ENV?.HUMANBEHAVIOR_API_KEY;
if (apiKey) {
  HumanBehaviorTracker.init(apiKey, {
    ingestionUrl: window.ENV?.HUMANBEHAVIOR_INGESTION_URL,
  });
}

startTransition(() => {
  hydrateRoot(
    document,
    <StrictMode>
      <RemixBrowser />
    </StrictMode>,
  );
});

entry.client.tsx only runs in the browser. Do not import the tracker in entry.server.tsx.

React Router 7 note

The wizard treats @react-router/dev as Remix-family. The same rule applies:

  • put SDK init in the browser entry
  • hand env values to the browser intentionally
  • do not initialize from server loaders/actions

Adjust imports (RemixBrowser vs framework-specific browser entry) to your app's generated files.

Why the env names are not VITE_

Remix env exposure is app-defined. The wizard config uses plain:

NameWhy
HUMANBEHAVIOR_API_KEYServer reads it, then the app chooses to expose it
HUMANBEHAVIOR_INGESTION_URLOptional override, also explicitly exposed

If your Remix app already has a different public env convention, adapt the names but keep the values visible to entry.client.tsx.

Options example

HumanBehaviorTracker.init(apiKey, {
  ingestionUrl: window.ENV?.HUMANBEHAVIOR_INGESTION_URL,
  release: window.ENV?.APP_RELEASE,
  environment: window.ENV?.APP_ENVIRONMENT,
  redactionStrategy: { mode: "visibility-first" },
});

Only add APP_RELEASE / APP_ENVIRONMENT to window.ENV if you intentionally expose them.

Remix client transitions use browser history, so the SDK records page navigation automatically.

If you have a custom client router wrapper, retain the tracker and call:

void tracker.trackPageView();

after route changes.

Common pitfalls

PitfallSymptomFix
Reading process.env directly in entry.client.tsxUndefined in browserSerialize through window.ENV
Importing SDK in entry.server.tsxServer/DOM errorsKeep SDK import in entry.client.tsx
Forgetting to escape/serialize env safelyBroken document scriptUse JSON.stringify
Treating the API key as a private server secretConfusionBrowser SDK keys are public project write credentials; still never commit them
Using generic React provider rootHydration riskPrefer direct browser entry init

Wizard config summary

Wizard fieldValue
Integrationremix
Package signals@remix-run/react, @react-router/dev
Env varsHUMANBEHAVIOR_API_KEY, HUMANBEHAVIOR_INGESTION_URL
Reference entryapp/entry.client.tsx
Project prompt"Remix == React Router 7"

Verify

  1. Restart the Remix server after env changes.
  2. Open the app in a browser.
  3. Confirm window.ENV.HUMANBEHAVIOR_API_KEY exists without exposing unrelated secrets.
  4. Confirm ingestion requests return 2xx.
  5. Navigate with Remix links and open Replays.

Full guide: Verify data is flowing.