Skip to main content
Scaffold + manual step. Remix doesn’t expose environment variables to the browser automatically — you have to surface them via a loader. The CLI writes the LucentInit component but leaves the loader wiring to you.

Install with the CLI

npx @lucenthq/cli init luc_pk_...
Pick Remix when prompted.

Manual setup

1

Environment variable

.env:
LUCENT_PUBLIC_KEY=luc_pk_...
2

LucentInit component

app/components/lucent-init.tsx:
"use client";

import { useEffect } from "react";
import { LucentTracker } from "@lucenthq/sdk";

let tracker: LucentTracker | null = null;

export function LucentInit({ publicKey }: { publicKey: string }) {
  useEffect(() => {
    if (!tracker) {
      tracker = new LucentTracker({ publicKey });
      tracker.start();
    }
    return () => {
      tracker?.stop();
      tracker = null;
    };
  }, [publicKey]);

  return null;
}
3

Expose the key via the root loader (manual)

app/root.tsx:
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json, useLoaderData } from "@remix-run/react";
import { LucentInit } from "~/components/lucent-init";

export async function loader({ request }: LoaderFunctionArgs) {
  return json({
    ENV: {
      LUCENT_PUBLIC_KEY: process.env.LUCENT_PUBLIC_KEY,
    },
  });
}

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

  return (
    <html>
      <body>
        <LucentInit publicKey={ENV.LUCENT_PUBLIC_KEY!} />
        {/* ...your routes */}
      </body>
    </html>
  );
}

Notes

  • Remix never ships process.env to the browser. The loader pattern is the canonical way to expose public keys; don’t try to read process.env directly from a client component.
  • LucentInit renders nothing — it only exists to run the tracker inside a useEffect so it initializes on mount and stops on unmount.
  • Prefix the env var with LUCENT_ (no public prefix) because it’s consumed server‑side by the loader before being handed to the client.