remix: Uncaught ReferenceError: process is not defined | Using env variables with Vite

Reproduction

I checked the console and the only error I have is this:

Uncaught ReferenceError: process is not defined
    at node_modules/util/util.js (@remix-run_node.js?v=2071f225:764:5)
    at __require (chunk-WXXH56N5.js?v=2071f225:12:50)
    at node_modules/stream-slice/index.js (@remix-run_node.js?v=2071f225:5672:16)
    at __require (chunk-WXXH56N5.js?v=2071f225:12:50)
    at node_modules/@remix-run/node/dist/upload/fileUploadHandler.js (@remix-run_node.js?v=2071f225:5869:23)
    at __require (chunk-WXXH56N5.js?v=2071f225:12:50)
    at node_modules/@remix-run/node/dist/index.js (@remix-run_node.js?v=2071f225:6057:29)
    at __require (chunk-WXXH56N5.js?v=2071f225:12:50)
    at @remix-run_node.js?v=2071f225:6159:16

vite.config.ts:

import { unstable_vitePlugin as remix } from "@remix-run/dev";
import { ConfigEnv, defineConfig, loadEnv } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default ({ mode }: ConfigEnv) => {
  // Here we add env vars from .env files to process.env.
  // Note the last arg is a blank string so that all env vars
  // are loaded, not just those starting with "VITE_"
  Object.assign(process.env, loadEnv(mode, process.cwd(), ""));

  return defineConfig({
    plugins: [
      remix({
        ignoredRouteFiles: ["**/.*"],
        serverModuleFormat: "cjs",
      }),
      tsconfigPaths(),
    ],
  });
};

How I’m accessing the env variables is as per the documentation: https://remix.run/docs/en/main/guides/envvars

root.tsx:

export async function loader({ request }: LoaderFunctionArgs) {
  const isAnonymous = await isAnonymousSession(request);

  if (isAnonymous) {
    return response.ok(
      {
        env: getBrowserEnv(),
        email: null,
        userTier: null,
      },
      { authSession: null }
    );
  }

  const authSession = await requireAuthSession(request);
  const { userId, email } = authSession;

  try {
    const userTier = await getUserTier(userId);

    return response.ok(
      {
        env: getBrowserEnv(),
        email,
        userTier,
      },
      { authSession }
    );
  } catch (cause) {
    throw response.error(cause, { authSession });
  }
}
export default function App() {
  const { env } = useLoaderData<typeof loader>();

  return (
    <html className="h-full" lang="en">
      <head>
        <Meta />
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width,initial-scale=1.0,maximum-scale=1.0"
        />
        <Links />
      </head>
      <body className="h-full">
        <Content />
        <ScrollRestoration />
        <script
          dangerouslySetInnerHTML={{
            __html: `window.env = ${JSON.stringify(env)}`,
          }}
        />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

env.ts:

import { Currency } from "@prisma/client";
import { z } from "zod";

import { OneMixError } from "~/utils/error";
import { isBrowser } from "./misc";

declare global {
  interface Window {
    env: {
      SUPABASE_URL: string;
      SUPABASE_ANON_PUBLIC: string;
      DEFAULT_CURRENCY: Currency;
    };
  }
}

declare global {
  namespace NodeJS {
    interface ProcessEnv {
      SUPABASE_URL: string;
      SUPABASE_SERVICE_ROLE: string;
      SERVER_URL: string;
      SUPABASE_ANON_PUBLIC: string;
      SESSION_SECRET: string;
      STRIPE_SECRET_KEY: string;
      STRIPE_ENDPOINT_SECRET: string;
      DEFAULT_CURRENCY: Currency;
    }
  }
}

type EnvOptions = {
  isSecret?: boolean;
  isRequired?: boolean;
};
function getEnv(
  name: string,
  { isRequired, isSecret }: EnvOptions = { isSecret: true, isRequired: true }
) {
  if (isBrowser && isSecret) return "";

  const source = (isBrowser ? window.env : process.env) ?? {};

  const value = source[name as keyof typeof source] || "";

  if (!value && isRequired) {
    throw new OneMixError({
      message: `Env "${name}" is not set`,
    });
  }

  return value;
}

/**
 * Server env
 */
export const SERVER_URL = getEnv("SERVER_URL");
export const SUPABASE_SERVICE_ROLE = getEnv("SUPABASE_SERVICE_ROLE");
export const SESSION_SECRET = getEnv("SESSION_SECRET");
export const STRIPE_SECRET_KEY = getEnv("STRIPE_SECRET_KEY");
export const STRIPE_ENDPOINT_SECRET = getEnv("STRIPE_ENDPOINT_SECRET");

/**
 * Shared envs
 */
export const NODE_ENV = getEnv("NODE_ENV", {
  isSecret: false,
  isRequired: false,
});
export const SUPABASE_URL = getEnv("SUPABASE_URL", { isSecret: false });
export const SUPABASE_ANON_PUBLIC = getEnv("SUPABASE_ANON_PUBLIC", {
  isSecret: false,
});
export const DEFAULT_CURRENCY = z
  .nativeEnum(Currency)
  .parse(getEnv("DEFAULT_CURRENCY", { isSecret: false }));

export function getBrowserEnv() {
  return {
    SUPABASE_URL,
    SUPABASE_ANON_PUBLIC,
    DEFAULT_CURRENCY,
  };
}

System Info

System:
    OS: Windows 11 10.0.22631
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
    Memory: 1.00 GB / 7.86 GB
  Binaries:
    Node: 20.9.0 - D:\Node\node.EXE
    npm: 10.2.4 - ~\AppData\Roaming\npm\npm.CMD
    pnpm: 8.11.0 - ~\AppData\Local\pnpm\pnpm.EXE
  Browsers:
    Edge: Chromium (120.0.2210.121)
    Internet Explorer: 11.0.22621.1
  npmPackages:
    @remix-run/dev: ^2.4.0 => 2.4.1 
    @remix-run/node: ^2.4.0 => 2.4.1 
    @remix-run/react: ^2.4.0 => 2.4.1 
    @remix-run/serve: ^2.4.0 => 2.4.1 
    vite: ^5.0.0 => 5.0.10

Used Package Manager

npm

Expected Behavior

There should be a process is not defined error.

Actual Behavior

There is a process is not defined error in the browser console. As a side effect of this, the onClick function for any of my buttons is not working.

About this issue

  • Original URL
  • State: closed
  • Created 6 months ago
  • Comments: 19 (6 by maintainers)

Most upvoted comments

Literally having the same exact issue as well. Issue is happening for me with this line: import { json, redirect } from "@remix-run/node

For anyone coming across this problem, I had a similar issue where I had a common utils.ts file with some server + client utils. I separated them to a utils.ts and utils.server.ts such that the latter was the only one that was importing:

import { json } from '@remix-run/node'

This seemed to have fixed the issue. Alternatively, I could have used the vite-env-only package to have one file and ensure the individual functions are nominated as client or server only. Two files made more sense for my project.

@toddheslin I had the same issue as @fabregas4.

The error occurs if you import either json or redirect and don’t actually use them in your loader/action. I hit this in development when I implemented a redirect, but then backed it out without actually removing the import as well.

@Pape98 @toddheslin

I’m a newbie so I might be off-track, but I have struggled with what seems a very similar issue over the last 24 hours. I finally resolved it (I think), at least for me.

I was trying to import { json } from '@remix-run/node' as well.

I was getting that same process var error in browser console.

I tried moving the import to a utils.server.js and then importing that from my route. This seemed to clear the browser console error, but now became a server console error “Internal server error: Server-only module referenced by client”.

What I eventually discovered is you have to USE the json method within your loader or action. If you do this, the error should clear, regardless of whether you import the package directly or via a .server file.

I was tripped up because I was actually importing both json and redirect, but only using one or the other in the action. The error only cleared when both were being used, or the one that wasn’t being used, was removed from the import.

Hope that makes sense and hope it helps.

Basically my conclusion is that if you import methods without using them, vite/remix assumes they are referenced by the client, whereas if you use them within loader/action, then it understands they are only referenced by the server. Perhaps someone from the team can comment?

Is there a way for the error message to track down where the issue is?

The error message comes from JS itself, not Remix. If Remix tries to handle the error, it would prevent users from setting up client-safe polyfills for the same imports so not much we can do without a substantially more complex approach.