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:
| Signal | Meaning |
|---|---|
@remix-run/react | Classic Remix app |
@react-router/dev | React 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-jsEnvironment variables
HUMANBEHAVIOR_API_KEY=your-api-key
HUMANBEHAVIOR_INGESTION_URL=https://ingest.humanbehavior.coRemix 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:
| Name | Why |
|---|---|
HUMANBEHAVIOR_API_KEY | Server reads it, then the app chooses to expose it |
HUMANBEHAVIOR_INGESTION_URL | Optional 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.
Navigation
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
| Pitfall | Symptom | Fix |
|---|---|---|
Reading process.env directly in entry.client.tsx | Undefined in browser | Serialize through window.ENV |
Importing SDK in entry.server.tsx | Server/DOM errors | Keep SDK import in entry.client.tsx |
| Forgetting to escape/serialize env safely | Broken document script | Use JSON.stringify |
| Treating the API key as a private server secret | Confusion | Browser SDK keys are public project write credentials; still never commit them |
| Using generic React provider root | Hydration risk | Prefer direct browser entry init |
Wizard config summary
| Wizard field | Value |
|---|---|
| Integration | remix |
| Package signals | @remix-run/react, @react-router/dev |
| Env vars | HUMANBEHAVIOR_API_KEY, HUMANBEHAVIOR_INGESTION_URL |
| Reference entry | app/entry.client.tsx |
| Project prompt | "Remix == React Router 7" |
Verify
- Restart the Remix server after env changes.
- Open the app in a browser.
- Confirm
window.ENV.HUMANBEHAVIOR_API_KEYexists without exposing unrelated secrets. - Confirm ingestion requests return 2xx.
- Navigate with Remix links and open Replays.
Full guide: Verify data is flowing.