If you use Pages router in NextJS there are multiple ways to get environment variables at runtime (tools like react-env/next-runtime-env, in addition to that envs are available in getStaticProps (read more in Craigs Wardmans blog about NextJS + docker)).
But how to do this when you are using server components in App router (available from NextJS 13)?
It is possible to build the NextJS project in the ENTRYPOINT of the Dockerfile, but I do not want the slow startup and large images. I guess a search-replace approach would also work for App router, but that feels a bit hacky.
In NextJS 13, using the App router, components may be either server or client components, which are rendered in separate environments.
You can access environment variables in both server and client components, but there are certain behaviours that you need to be aware of:
NEXT_PUBLIC_ (documentation)."use client" (documentation).force-dynamic in the example below). You can check whether a component is static or dynamic by observing the symbols to the left of the component in the build output. A circle ○ indicates a static component and a lambda λ indicates a dynamic component.Thus, if you wish to access an environment variable at run-time in a client component, you must pass it somehow from a server component. For example, if you wish to have the majority of an application run in the client environment and for it to use a run-time environment variable, you can wrap the whole body element in a client component as shown below. You could then run any client code in the ClientBody component, for example adding the environment variable to a React Context.
app/layout.jsx:
import ClientBody from "@/components/Body";
export const dynamic = "force-dynamic"
const myVar = process.env.NEXT_PUBLIC_MY_VAR;
export default function RootLayout ({
children,
}) {
console.log(process.env.NEXT_PUBLIC_MY_VAR, myVar);
return (
<html lang="en">
<ClientBody
myVar={myVar}
>
{children}
</ClientBody>
</html>
);
}
components/Body.jsx
"use client";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
export default function ClientBody({
myVar,
children
}) {
console.log(process.env.NEXT_PUBLIC_MY_VAR, myVar);
return (
<body>
<Header />
{children}
<Footer />
</body>
);
}
Supply your run-time value to Docker like so: docker run -e NEXT_PUBLIC_MY_VAR="..." ...
In the console output of the example, you will see that the value of process.env.NEXT_PUBLIC_MY_VAR will appear frozen at build-time in the client component, yet the value of prop myVar will be that of the run-time environment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With