We have an application that cannot support prerendering. Our _app.js is wrapped with an HOC that leverages Apollo's getDataFromTree to dynamically fetch GraphQL data.
With Next.js 9, we now always see the warning that we've opted out of prerendering, which is expected for us.
Warning: You have opted-out of Automatic Prerendering due to `getInitialProps` in `pages/_app`.
--
Jul 15 2019 09:16:00:234Read more: https://err.sh/next.js/opt-out-automatic-prerendering
The main problem is that because Next.js relies on heuristics of the code to determine whether or not prerendering can occur, it evaluates and executes code in our application that is not intended to be executed in a build context.
In the example code below, we rely on the CLOUDINARY_CLOUD_NAME environment variable to be set at runtime. Our infrastructure must maintain the ability to set this value at runtime and not build time. The variable is undefined at build time but the prerender heuristic executes the export and results in a hard error that prevents our app from building.
import getConfig from 'next/config';
import cloudinary from 'cloudinary-core';
const { publicRuntimeConfig } = getConfig();
const { CLOUDINARY_CLOUD_NAME } = publicRuntimeConfig;
export default cloudinary.Cloudinary.new({ cloud_name: CLOUDINARY_CLOUD_NAME });
Add a configuration option to next.config.js that allows developers to disable the heuristic check that executes to determine if automatic prerendering is feasible _and_ hides the warning about prerendering.
// next.config.js
module.exports = {
prerenderPages: false // alternative: automaticPrendering: false
};
This warning is harmless and will not fail the build -- adding a configuration option to snooze it seems unnecessary.
We've already found many users who had no idea their Custom App is de-optimizing their entire application (because they're using HOCs).
It's too valuable to allow it to be dismissed permanently, especially if it makes its way into examples or quick google searches.
We'll be iterating on this in the future to allow it to work in more scenarios!
@Timer You didn't address my actual problem though. The warning is harmless but the build failure is quite harmful. How will you be addressing that?
If you need to use runtime configuration you must de-optimize your entire application by adding a getInitialProps to your Custom App.
Runtime configuration is never encouraged in an application. Build-time configuration should always be used.
Are you saying that even with a de-optimized application you're getting an empty runtime object?
@Timer Yes, I realize you all do not encourage runtime config (anymore) and yes, I am getting an empty runtime object at build time, but not runtime. I can create a repro app if that helps you all understand what I am seeing.
Please, a reproduction would be great.
@Timer Here you go: https://github.com/goldenshun/nextjs-9-build-repro.
See README for repro instructions.
Ah, yes @goldenshun this is a known bug as I explained above.
If you need to use runtime configuration you must de-optimize your entire application by adding a
getInitialPropsto your Custom App.
Really sorry that this works accidentally during development -- see that bug report here: https://github.com/zeit/next.js/issues/7844
Your provided reproduction is suffering from a different user-error, which is that the files you are requiring have global side effects that throw errors.
Same here when upgrading a couple of projects that usenext-i18next. In those apps _app.tsx imports from i18n.ts, which in my case does something like this:
if (typeof window === "undefined") {
const { env } = require("./config");
languageDetector.addDetector({
name: "enforcedLocale",
lookup: () => env.ENFORCED_LOCALE,
cacheUserLanguage: _.noop,
});
}
Inside config.ts there is a use of envalid, which sanitises process.env and crashes the app if some required env variable is missing. That's by design.
Locally, I've got .env, thanks to which next dev, next build and next start work without problems. However, because next build in CI now runs _app and thus calls envalid.cleanEnv(...), the build crashes.
Here is the workaround that helped me:
- if (typeof window === "undefined") {
+ if (typeof window === "undefined" && process.env.NEXT_PHASE !== "phase-production-build") {
source • broken build • completed build after the workaround was applied
I agree with @goldenshun that an option to skip the preprendering attempt would be a good thing to have. That would be cleaner than having to rely on process.env.NEXT_PHASE here and there. Alternatively, Next could _statically_ search for getInitialProps in _app.tsx, i.e. without trying to execute this file.
I'm facing the same issue (after upgrade from 8 to 9 prerendeing fails due to empty publicRuntimeConfig).
I wonder whether it is safe to stub publicRuntimeConfig just to get the build to work again, and expect the app to work correctly, considering I've opted-out of prerendering? publicRuntimeConfig will be populated in runtime anyway.
any update on this?
It would great to have some sort of additional command to hydrate already built files with environmental specific variables; example:
1) build time.
$ npx next build
2) execution time (this should have to be fast, something that we can execute right before running the server)
$ npx next hydrate
On the other hand, ..... this is my very nasty workaround
_document.js
render() {
return (
<Html>
<Head>
<script src="/api/common/config.js" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
server.js:
app.get(`/api/common/config.js`, async (req, res) => {
res.type('.js');
return res.send(`window.app_config = { COGNITO_USERPOOL_ID: "${process.env.COGNITO_USERPOOL_ID}", COGNITO_CLIENT_ID: "${process.env.COGNITO_CLIENT_ID}", GOOGLEMAPS_API_KEY: "${process.env.GOOGLEMAPS_API_KEY}" };`);
});
The issue still exist. @Timer why it is closed?
I can't rely on .env check because it broke build!
As @kachkaev wrote above checking process.env.NEXT_PHASE work but it is nasty and painful to do that.
I have getInitialProps in _app.js but it fails despite it because it runs file. It should statically check it.
The ability to opt out of pre-rendering is a huge need. Pre-rendering cost based resources (i.e., a page loading MapBox for example - a problem in our application that I've traced to this feature) for 100% of visitors when only 10% of visitors actually visit the page is not an optimization. We need the ability to designate specific pages not to be pre-rendered, or to disable pre-rendering entirely.
We need the ability to designate specific pages not to be pre-rendered, or to disable pre-rendering entirely.
You can do so by using getServerSideProps: https://nextjs.org/docs/basic-features/pages#server-side-rendering
Most helpful comment
Same here when upgrading a couple of projects that use
next-i18next. In those apps_app.tsximports fromi18n.ts, which in my case does something like this:Inside
config.tsthere is a use ofenvalid, which sanitisesprocess.envand crashes the app if some required env variable is missing. That's by design.Locally, I've got
.env, thanks to whichnext dev,next buildandnext startwork without problems. However, becausenext buildin CI now runs_appand thus callsenvalid.cleanEnv(...), the build crashes.Here is the workaround that helped me:
source • broken build • completed build after the workaround was applied
I agree with @goldenshun that an option to skip the preprendering attempt would be a good thing to have. That would be cleaner than having to rely on
process.env.NEXT_PHASEhere and there. Alternatively, Next could _statically_ search forgetInitialPropsin_app.tsx, i.e. without trying to execute this file.