Use process.env
client-side with Remix
The process.env
thing is a Node.js-only feature that many front-end developers adopted as something to use in their client-side code.
Usually backed by a build tool like webpack inlining the env variables at build time, they're not actual env variables but more like build variables.
In Remix, they decided only to inline the value of process.env.NODE_ENV
because it's a commonly used variable in many libraries, including React, to optimize the code for production. The only way to access any other env variables is server-side, which means in your loaders and actions.
But we need to use one of these variables in a function used both client-side and server-side, for example, in the UI.
First, we need to return the variable from a loader.
export async function loader() {
return json({ variable: process.env.VARIABLE });
}
Then we could access it with useLoaderData
and pass it to our function.
export default function View() {
let { variable } = useLoaderData<typeof loader>();
doSomething(variable);
// ...
}
Doing it this way is ideal because our code doesn't depend on process.env
to be a thing, which for example, in Cloudflare works doesn't exist, and the only way to access env variables is from the loader/action context object.
export async function loader({ context }: LoaderArgs) {
return json({ variable: context.VARIABLE });
}
But let's decide we're using Node, and we still want to access variables directly as process.env.VARIABLE
in our code.
Remix docs suggest returning the env variables from the root route loader and using an inline script to make them globally available client-side.
//example copied from Remix docs
export async function loader() {
return json({ ENV: { VARIABLE: process.env.VARIABLE } });
}
export function Root() {
const data = useLoaderData();
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
<Outlet />
{/* This is the inline script tag */}
<script
dangerouslySetInnerHTML={{
__html: `window.ENV = ${JSON.stringify(data.ENV)}`,
}}
/>
<Scripts />
</body>
</html>
);
}
Doing this, we can do ENV.VARIABLE
client-side, but not server-side, so our function would have to detect if it's running on the server using process.env.VARIABLE
and otherwise use ENV.VARIABLE
, but here's the trick, we can set process
into the global scope too so that we can use process.env.VARIABLE
client-side too.
<script
dangerouslySetInnerHTML={{
__html: `window.process = ${JSON.stringify({
env: data.ENV,
})}`,
}}
/>
Now, our code can use window.process.env.VARIABLE
or just process.env.VARIABLE
, as in our server-side code.