Open Unsleeping opened 2 months ago
@HofmannZ Would love for you to take a look at this
Following up on my prior message, the real problem is this bug in Next.js: https://github.com/vercel/next.js/issues/69567#issuecomment-2334542530
@HofmannZ
The issue was discussed in a thread, and here’s a brief summary:
When rendering the not-found page, Next.js throws a specific error and reacts to all errors in the same way — it stops rendering the page, including the root layout where next-runtime-env configuration assignment occurs. As explained in the thread, the problem is that Next.js halts the rendering of any page when an error occurs, whether it’s a NotFoundError
or another error. As a result, the server stops rendering the page, including all layout elements. It’s important to note that the initial HTML returned by the server does not contain any specific layout elements.
Basically, the rendering of a page is stopped whenever any error is encountered. Regardless of whether the error is caused by notFound()
or a regular new Error()
, the route rendering stops, and the HTML returned by the server lacks the necessary tags for scripts using the beforeInteractive
strategy. When hydration finishes, the script is added to the HTML, but by that time, the main app-bootstrap script has already executed, and its impact is lost. Therefore, the beforeInteractive
loading strategy does not work on pages with errors.
However, all other loading strategies, such as afterInteractive
or lazyOnLoad
, work correctly.
I’m actively testing the afterInteractive
strategy right now and haven’t encountered any bugs yet. They may surface soon, but there are bugs #145 and #147 related to similar behavior.
That’s why I kept beforeInteractive
as the default but allowed the option to change it per project if necessary.
We need to look into this more deeply and possibly switch from beforeInteractive
to afterInteractive
, instead of providing the option to choose the strategy
prop for the script.
@HofmannZ
The only issue I've encountered so far after switching to afterInteractive
is when I use runtime environment variables to set constants that are later used throughout the application. There are cases where, on the client side, the constant is initialized before the environment configuration is populated into the global window
object.
For example, something like this:
export const apiUrl = env("NEXT_PUBLIC_API_URL");
In this case, window.__ENV
might not be available yet, causing the constant to be set with an empty value. On the server side, this isn’t an issue since env(key)
just returns process.env[key]
. But on the client side, it’s recommended to retrieve environment variables at the point of use, using a getter function to ensure the values are available when needed.
In the Next.js documentation for the beforeInteractive
prop, it clearly states:
Load the script before any Next.js code and before any page hydration occurs.
This explains why the previous behavior wasn't breaking, but after switching to afterInteractive
, it caused issues in my case.
p.s. same with lazyOnload
strategy
This pull request enhances the
next-runtime-env
library by adding support for thestrategy
prop from the Next.jsnext/script
component. The change addresses issues #145 and #147, and helps improve runtime environment configuration retrieval on the not-found page.Key Changes:
strategy
prop to allow customization of script loading behavior (e.g.,beforeInteractive
,afterInteractive
).beforeInteractive
, following current practices. This ensures backward compatibility, but the default strategy can now be adjusted as needed.Observations:
During testing, I noticed that with the
beforeInteractive
strategy, I was unable to retrieve the runtime environment configuration on thenot-found
page. When using afterInteractive, the environment config is successfully retrieved and assigned towindow
on thenot-found
page.So, I need to know about the exact limitations of the
beforeInteractive
strategy, as it seems hardcoded to load scripts early. I might have misunderstood something, and further clarification would be helpful.For reference, here's the relevant Next.js documentation on the beforeInteractive strategy: Next.js Script beforeInteractive Documentation.