How do I set up dynamic imports correctly (for beyond localhost)? - node.js

I followed https://docs.meteor.com/packages/dynamic-import.html to set up dynamic imports, and it works fine on localhost.
For context, I am creating a blog (Meteor/React/Apollo) which renders MDX files, and these files need to be imported, so I have a list of all my posts as such:
import("./imports/posts/61a000d03a1931b8819dc17e.mdx")
import("./imports/posts/619cae2f03f4ff710aa3d980.mdx")
import("./imports/posts/619e002d386ebf2023ea85c3.mdx")
import("./imports/posts/619fff7c5b312d7622acda86.mdx")
I have a Post.jsx component:
import React, { useState, useRef } from "react"
import { useHistory, useParams } from "react-router-dom"
import { useQuery } from "#apollo/client"
import { GET_POST_ID } from "../../api/posts/queries"
const Post = () => {
const Post = useRef()
const history = useHistory()
const { slug } = useParams()
const [loadedPost, setLoaded] = useState(false)
const [viewer, showViewer] = useState(false)
const open = () => showViewer(true)
const { data, loading, error } = useQuery(GET_POST_ID, { variables: { slug }})
if (loading) return null
if (error) {
console.log(error)
return null
}
import(`./posts/${data._id}.mdx`).then(MDX => {
Post.current = MDX.default
setLoaded(true)
}, (err) => {
console.log(err)
})
return loadedPost ? (
<>
<div className="postContent">
<div className="markdownOverride markdown-body">
<Post.current />
</div>
</div>
</>
) : null
}
export default Post
This works well and good on my local network. However, if I attempt to access it from outside my local network, an error is thrown in the console that all the blog modules are not found. The Apollo/GraphQL portion works fine, but the actual module can't be imported.
How do I get this to work outside of localhost?
Thanks.
EDIT: The error messages are, for each post:
Uncaught (in promise) Error: Cannot find module '/imports/posts/61a000d03a1931b8819dc17e.mdx`
And when I load the actual post page:
Uncaught (in promise) TypeError: Failed to fetch

Isn't your error thrown by console.log(err) ?
import(`./posts/${data._id}.mdx`).then(MDX => {
Post.current = MDX.default
setLoaded(true)
}, (err) => {
console.log(err) // <---- here
})
This means your path isn't right for /imports/posts/61a000d03a1931b8819dc17e.mdx.
To me you can't use changing parameters when doing dynamic imports.
./posts/${data._id}.mdx, because your meteor or webpack compilation needs to treat all the data._ids avalaible in your database in order to compile and prepare the file...
This might be why it works in development mode but not in production.
You can just do dynamic imports of modules or components (already compiled), no more to me. Take a look at your output compilation bundles, and try to find where are your components...

It turns out that I needed to specify the ROOT_URL correctly when initializing Meteor. With an ngrok http tunnel on port 3000 pointing to https://some-hash.ngrok.io, I had to start Meteor with: ROOT_URL="https://some-hash.ngrok.io" meteor. When I do this, I can access it fine and everything loads from my local IP and the ngrok URL, but I can't seem to load it up from localhost (it times out).
Specifying my local or public IP did not work, I could not get it to load through any of those methods.

Related

Firebase Realtime Database not working on NextJS only after build - Maximum call stack size exceeded

I used Firebase Realtime Database with Next.js, for back-end.
And I wrote the below code. But It works only in local, not in production. The reason I comments some lines as follows is that I want to show that the error was not caused by my code. onValue, get, set, update were all the same. The same error was raised. And also I confirmed that effect function in useEffect() runs only once.
/* lib/firebase.ts */
import { initializeApp } from 'firebase/app';
import { getDatabase } from 'firebase/database';
// Initialize Firebase
const firebaseConfig = {
...
};
const app = initializeApp(firebaseConfig);
// Initialize Realtime Database
export const database = getDatabase(app);
import { onValue, ref, update } from 'firebase/database';
import { database } from 'lib/firebase';
...
useEffect(() => {
onValue(
ref(database, `objects/${user.uid}`),
(snapshots) => {
console.log(snapshots);
// const fetchedObjects: Common.Object[] = [];
// snapshots.forEach((snapshot) => {
// fetchedObjects.push({ key: snapshot.key, ...snapshot.val() } as Common.Object);
// });
// setTimeout(() => {
// setObjects(fetchedObjects);
// setIsObjectLoaded(true);
// }, 500);
}
);
}, []);
As a result of console.log(), a snapshot object with no children was returned. But It should be a snapshot object with serveral child nodes according to database state. It is the case in local.
Only in production, the below error was raised. _app-e9aaf40698fc4780.js was bundled by Next.js, so It was so complicated that I couldn't read it. But when I deduce from comments, maybe it can be firebase/database code.
Uncaught RangeError: Maximum call stack size exceeded
at i (_app-e9aaf40698fc4780.js:1486:770)
at i (_app-e9aaf40698fc4780.js:1486:897)
at i (_app-e9aaf40698fc4780.js:1486:906)
at i (_app-e9aaf40698fc4780.js:1486:897)
at i (_app-e9aaf40698fc4780.js:1486:906)
at i (_app-e9aaf40698fc4780.js:1486:897)
at i (_app-e9aaf40698fc4780.js:1486:906)
at i (_app-e9aaf40698fc4780.js:1486:897)
at i (_app-e9aaf40698fc4780.js:1486:906)
at i (_app-e9aaf40698fc4780.js:1486:897)
1486 line's code is here.
*/ let e3,e4,e6=new class extends eW{compare(e,t){let n=e.node.getPriority(),r=t.node.getPriority(),i=n.compareTo(r);return 0===i?q(e.name,t.name):i}isDefinedOn(e){return!e.getPriority().isEmpty()}indexedValueChanged(e,t){return!e.getPriority().equals(t.getPriority())}minPost(){return eq.MIN}maxPost(){return new eq(j,new e2("[PRIORITY-POST]",e4))}makePost(e,t){let n=e3(e);return new eq(t,new e2("[PRIORITY-POST]",n))}toString(){return".priority"}},e5=Math.log(2);class e8{constructor(e){var t;this.count=parseInt(Math.log(e+1)/e5,10),this.current_=this.count-1;let n=parseInt(Array(this.count+1).join("1"),2);this.bits_=e+1&n}nextBitIsOne(){let e=!(this.bits_&1<<this.current_);return this.current_--,e}}let e9=function(e,t,n,r){e.sort(t);let i=function(t,r){let s=r-t,o,a;if(0===s)return null;if(1===s)return o=e[t],a=n?n(o):o,new eG(a,o.node,eG.BLACK,null,null);{let l=parseInt(s/2,10)+t,u=i(t,l),c=i(l+1,r);return o=e[l],a=n?n(o):o,new eG(a,o.node,eG.BLACK,u,c)}},s=new e8(e.length),o=function(t){let r=null,s=null,o=e.length,a=function(t,r){let s=o-t;o-=t;let a=i(s+1,o),u=e[s],c=n?n(u):u;l(new eG(c,u.node,r,null,a))},l=function(e){r?(r.left=e,r=e):(s=e,r=e)};for(let u=0;u<t.count;++u){let c=t.nextBitIsOne(),h=Math.pow(2,t.count-(u+1));c?a(h,eG.BLACK):(a(h,eG.BLACK),a(h,eG.RED))}return s}(s);return new eY(r||t,o)},e7,te={};class tt{constructor(e,t){this.indexes_=e,this.indexSet_=t}static get Default(){return(0,f.hu)(te&&e6,"ChildrenNode.ts has not been loaded"),e7=e7||new tt({".priority":te},{".priority":e6})}get(e){let t=(0,f.DV)(this.indexes_,e);if(!t)throw Error("No index defined for "+e);return t instanceof eY?t:null}hasIndex(e){return(0,f.r3)(this.indexSet_,e.toString())}addIndex(e,t){(0,f.hu)(e!==e$,"KeyIndex always exists and isn't meant to be added to the IndexMap.");let n=[],r=!1,i=t.getIterator(eq.Wrap),s=i.getNext();for(;s;)r=r||e.isDefinedOn(s.node),n.push(s),s=i.getNext();let o;o=r?e9(n,e.getCompare()):te;let a=e.toString(),l=Object.assign({},this.indexSet_);l[a]=e;let u=Object.assign({},this.indexes_);return u[a]=o,new tt(u,l)}addToIndexes(e,t){let n=(0,f.UI)(this.indexes_,(n,r)=>{let i=(0,f.DV)(this.indexSet_,r);if((0,f.hu)(i,"Missing index implementation for "+r),n===te){if(!i.isDefinedOn(e.node))return te;{let s=[],o=t.getIterator(eq.Wrap),a=o.getNext();for(;a;)a.name!==e.name&&s.push(a),a=o.getNext();return s.push(e),e9(s,i.getCompare())}}{let l=t.get(e.name),u=n;return l&&(u=u.remove(new eq(e.name,l))),u.insert(e,e.node)}});return new tt(n,this.indexSet_)}removeFromIndexes(e,t){let n=(0,f.UI)(this.indexes_,n=>{if(n===te)return n;{let r=t.get(e.name);return r?n.remove(new eq(e.name,r)):n}});return new tt(n,this.indexSet_)}}/**
I can't interpret the code, and I can't figure out the cause, so I'm going crazy. Help me.
+) I also tested on the new Next.js project created by create-next-app.
npx create-next-app#latest --typescript
cd {project folder name}
npm install firebase
And the code I wrote is here.
import "../styles/globals.css";
import type { AppProps } from "next/app";
import { useEffect } from "react";
import { initializeApp } from "firebase/app";
import { getDatabase, onValue, ref } from "firebase/database";
function MyApp({ Component, pageProps }: AppProps) {
useEffect(() => {
const firebaseConfig = {
...
};
const app = initializeApp(firebaseConfig);
const db = getDatabase(app);
// It works only in local
onValue(ref(db, "???"), (snapshots) => console.log(snapshots));
}, []);
return <Component {...pageProps} />;
}
export default MyApp;
As a result, like my project, the same error was raised. But It works well in local.
I'm the author of this post.
I resolved this problem by downgrading Next.js version from 12.3.1 to 12.3.0.

NextJS useRouter() returns null in unit test with Storybook add-on

I'm running into an issue when integrating my stories into my unit tests using Jest & React Testing Library in a NextJS app. In Storybook, my NextJS components render without issue with the Storybook Addon Next Router working as expected. However, in my Jest test file, my test throws an error because useRouter() returns null. I believe I've setup all the addons correctly.
My setup:
NextJS 12
Jest & React Testing Library
Storybook
Storybook Addon Next Router (for using Next router inside Storybook)
#storybook/testing-react (for integrating my stories, args & params with testing library)
The problem:
I've setup all the files according to the docs. The story renders fine inside Storybook and the useRouter() works thanks to the Storybook Next addon. However, the composeStories() function from #storybook/testing-react seems to fail to properly initialize the Next router Provider from the first addon. My unit test fails with the following error:
Test suite failed to run
TypeError: Cannot read property 'locale' of null
And points to the following line inside my component:
// Events.tsx
const { locale = 'en' } = useRouter();
This is my test file:
// Events.test.tsx
import React from 'react';
import { render, screen } from '#testing-library/react';
import { composeStories } from '#storybook/testing-react';
import * as stories from './Events.stories'
const { WithSeasonsAndEvents } = composeStories(stories);
describe('Events Screen', ()=> {
render(<WithSeasonsAndEvents />);
it('renders input data', ()=> {
// set up test
});
});
And my stories file:
// Events.stories.tsx
import React from 'react';
import { ComponentStory, ComponentMeta } from '#storybook/react';
import Events from './Events';
export default {
title: 'Events/Events Screen',
component: Events
} as ComponentMeta<typeof Events>;
const Template: ComponentStory<typeof Events> = (args) => <Events {...args} />;
export const WithSeasonsAndEvents = Template.bind({});
WithSeasonsAndEvents.args = {
// many args here
};
This story renders fine in Storybook. useRouter() works as expected inside my story and I am able to use all of its properties including pathname and locale. However, for some reason the useRouter() function returns null when the composed story is being rendered by React Testing Library.
What I have tried:
Verified installing #storybook/testing-react and set up my global configuration in jest setup file:
// jest-setup.ts
import '#testing-library/jest-dom/extend-expect';
import { setGlobalConfig } from '#storybook/testing-react';
// Storybook's preview file location
import * as globalStorybookConfig from './.storybook/preview';
setGlobalConfig(globalStorybookConfig);
Verified that jest reads my setup file and finds my Storybook preview file
Verified both .storybook/preview.js and .storybook/main.js match the usage guide:
preview.js
// .storybook/preview.js
import '../styles/tailwind.css';
import { RouterContext } from "next/dist/shared/lib/router-context"; // next 12 https://storybook.js.org/docs/react/writing-stories/parameters
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
nextRouter: {
Provider: RouterContext.Provider,
locale: 'en',
},
}
main.js
// .storybook/main.js
module.exports = {
"stories": [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.#(js|jsx|ts|tsx)",
"../components/**/*.stories.#(js|jsx|ts|tsx|mdx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials",
"storybook-addon-next-router",
],
"framework": "#storybook/react"
}
Stories render fine in Storybook, all useRouter() features work, and when I console.log the useRouter() return value, I get the full suite of NextJS useRouter object properties:
// >> console.log(useRouter()); inside Storybook
{
route: "/",
pathname: "/",
query: {},
asPath: "/",
events: {},
isFallback: false,
locale: 'en'
}
However, on the unit test, when logging the return value of useRouter(); inside my component, it returns null. Understandably, because it is null, the { locale } variable assignment is throwing an error in my unit test.
When logging the value of useRouter variable, both inside my Storybook preview and inside my unit test I get the following function:
// >> console.log(useRouter.toString())
function useRouter() {
return _react.default.useContext(_routerContext.RouterContext);
}
Does anyone have any idea on what can be going wrong? I'm fairly new to Storybook but I've tried looking through GitHuB issues and online and haven't been able to solve. No idea why useRouter() returns null inside Jest if the composeStories() fn should take care of resolving my Storybook params & args. Any insight would be appreciated.
I was running with the same issue. I solved it by exporting WithNextRouter as a decorator on preview.js
// preview.js
import { WithNextRouter } from 'storybook-addon-next-router/dist/decorators';
export const decorators = [WithNextRouter];

Renderer process out of memory after app is packaged into EXE

Expected Behavior
In my main renderer window I'm initializing UI from an imported module. When running the app in dev mode everything works but once I package the app into EXE and try to run it, I'm getting renderer process out of memory error.
The expected behavior is that the packaged app should run like the dev mode.
Actual Behavior
I've checked the electron logs and memory consumption in windows task manager. The memory usage keeps increasing until the renderer process crashes and electron logs oom error.
Since I'm new to electron I've read the documentation but unable to figure out why the same thing is running perfectly in dev mode and crashes in prod.
Log : Renderer process oom - see https://www.electronjs.org/docs/tutorial/application-debugging for potential debugging information.
Another log after some time : [19156:0921/120104.184:ERROR:gpu_init.cc(440)] Passthrough is not supported, GL is swiftshader
So this is what I'm trying to do -
Import my module which is served on localhost:
This module exposes an object - Tesseract through which I initialize my UI.
In the component which is rendered in my main browser window I initialize the UI like this:
File - index.tsx
useEffect(() => {
if (tsToken) {
Tesseract.ui
.init(tsToken.tsToken, "random_id")
.then((sdk: any) => {
console.log("🚀 ~ file: index.js ~ line 3 ~ sdk", sdk);
// After this line nothing is logged and renderer process crashes (But works when I run electron in dev mode)
sdk.init({
...MasterConfig,
RootElement: document.getElementById("tesseract-root"),
});
const authToken = sessionStorage.getItem("ts_token");
ipcRenderer.send("set-globals", { TAC: authToken });
})
.catch((err: any) => console.error(err));
}
}, [tsToken]);
return <></>;
In the above piece of code we pass a root element to the init method. The init method then initializes a react app in that root element.
EDIT:
This is the init method in that module
SDK.prototype.init = async function (config) {
try {
const parsedConfig = JSON.parse(this.META.uiConfig);
window.Tesseract.ui["MasterConfig"] = merge(parsedConfig, config);
window.Tesseract.ui["info"] = this.META;
const projectType = window.Tesseract.ui.MasterConfig.ProjectConfig.type;
console.log("🚀 ~ file: lib.js ~ line 51 ~ projectType", projectType)
const MasterConfig = window.Tesseract.ui["MasterConfig"];
console.log(MasterConfig);
if (
MasterConfig &&
MasterConfig.UserProperties &&
MasterConfig.UserProperties.userId
) {
console.log("React init here");
// Console logs upto this point and then everything fails
// No error logged
try {
RecorderUI.ReactApp({ ...parsedConfig, ...config }).then((obj) => {
// This is never logged
console.log("UI rendered");
return obj;
}).catch(err => console.log(err));
} catch (err) {
console.log(err);
return err;
}
} else {
const e = new Error("User Id Missing");
e.name = "Missing unique user Id";
throw e;
}
} catch (err) {
console.error(err);
return err;
}
};
This is the UI rendering method:
export default async function entry(config) {
// This is never logged so basically something is happening before this
console.log("somemmthing something");
console.log("Root Element: ",
window.Tesseract?.ui?.MasterConfig?.RootElement);
try {
const { RootElement } = config;
RootElement.attachShadow({ mode: "open" });
await IDBStore.create({
version: 1,
name: "ext",
schema: {
globals: "&key",
},
});
const shadowRoot = RootElement.shadowRoot;
const styleElement = document.createElement("style");
styleElement.innerHTML = `${LoaderCss} ${SourcePreviewCSS} ${NotificationCss} ${LibraryCss} ${SelectModeCss} ${WebcamBoxCss} ${CameraBubbleCss} ${countdownCss} ${indexStyles} ${commonCss} ${colorsCss} ${homeCss} ${recordCss} ${advancedOptionsCss} ${recordingTypesCss} ${toggleOptionsCss} ${tourOverlayCss} ${headerCss} ${checkboxCss} ${tooltipCss} ${dropdownCss} ${toggleCss} ${recordButtonCss} ${bannersCss} ${toolsCss} ${canvasCss} ${paintCss} ${recordingControlsCss} ${controlsMainCss}`;
const reactRoot = document.createElement("div");
reactRoot.setAttribute("id", "react-root");
shadowRoot.appendChild(styleElement);
shadowRoot.appendChild(reactRoot);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
reactRoot
);
window.addEventListener("message", handleMessaging, false);
} catch (err) {
console.error(err);
return new Error(err);
}
}
I've read numerous blog posts, github issues but unable to find what's causing this. I need help in determining what is the difference in internal workings of electron which causes such difference b/w running in dev mode vs. packaging the app.

Module not found: Can't resolve 'fs' in Next.js application

Unable to identify what's happening in my next.js app. As fs is a default file system module of nodejs. It is giving the error of module not found.
If you use fs, be sure it's only within getInitialProps or getServerSideProps. (anything includes server-side rendering).
You may also need to create a next.config.js file with the following content to get the client bundle to build:
For webpack4
module.exports = {
webpack: (config, { isServer }) => {
// Fixes npm packages that depend on `fs` module
if (!isServer) {
config.node = {
fs: 'empty'
}
}
return config
}
}
For webpack5
module.exports = {
webpack5: true,
webpack: (config) => {
config.resolve.fallback = { fs: false };
return config;
},
};
Note: for other modules such as path, you can add multiple arguments such as
{
fs: false,
path: false
}
I spent hours on this and the solution is also here on Stackoverflow but on different issue -> https://stackoverflow.com/a/67478653/17562602
Hereby I asked for MOD permission to reshare this, since this issue is the first one to show up on Google and probably more and more people stumble would upon the same problem as I am, so I'll try to saved them some sweats
Soo, You need to add this in your next.config.js
module.exports = {
future: {
webpack5: true, // by default, if you customize webpack config, they switch back to version 4.
// Looks like backward compatibility approach.
},
webpack(config) {
config.resolve.fallback = {
...config.resolve.fallback, // if you miss it, all the other options in fallback, specified
// by next.js will be dropped. Doesn't make much sense, but how it is
fs: false, // the solution
};
return config;
},
};
It works for like a charm for me
Minimal reproducible example
A clean minimal example will be beneficial to Webpack beginners since auto splitting based on usage is so mind-blowingly magic.
Working hello world baseline:
pages/index.js
// Client + server code.
export default function IndexPage(props) {
return <div>{props.msg}</div>
}
// Server-only code.
export function getStaticProps() {
return { props: { msg: 'hello world' } }
}
package.json
{
"name": "test",
"version": "1.0.0",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "12.0.7",
"react": "17.0.2",
"react-dom": "17.0.2"
}
}
Run with:
npm install
npm run dev
Now let's add a dummy require('fs') to blow things up:
// Client + server code.
export default function IndexPage(props) {
return <div>{props.msg}</div>
}
// Server-only code.
const fs = require('fs')
export function getStaticProps() {
return { props: { msg: 'hello world' } }
}
fails with:
Module not found: Can't resolve 'fs'
which is not too surprising, since there was no way for Next.js to know that that fs was server only, and we wouldn't want it to just ignore random require errors, right? Next.js only knows that for getStaticProps because that's a hardcoded Next.js function name.
OK, so let's inform Next.js by using fs inside getStaticProps, the following works again:
// Client + server code.
export default function IndexPage(props) {
return <div>{props.msg}</div>
}
// Server-only code.
const fs = require('fs')
export function getStaticProps() {
fs
return { props: { msg: 'hello world' } }
}
Mind equals blown. So we understand that any mention of fs inside of the body of getStaticProps, even an useless one like the above, makes Next.js/Webpack understand that it is going to be server-only.
Things would work the same for getServerSideProps and getStaticPaths.
Higher order components (HOCs) have to be in their own files
Now, the way that we factor out IndexPage and getStaticProps across different but similar pages is to use HOCs, which are just functions that return other functions.
HOCs will normally be put outside of pages/ and then required from multiple locations, but when you are about to factor things out to generalize, you might be tempted to put them directly in the pages/ file temporarily, something like:
// Client + server code.
import Link from 'next/link'
export function makeIndexPage(isIndex) {
return (props) => {
return <>
<Link href={isIndex ? '/index' : '/notindex'}>
<a>{isIndex ? 'index' : 'notindex'}</a>
</Link>
<div>{props.fs}</div>
<div>{props.isBlue}</div>
</>
}
}
export default makeIndexPage(true)
// Server-only code.
const fs = require('fs')
export function makeGetStaticProps(isBlue) {
return () => {
return { props: {
fs: Object.keys(fs).join(' '),
isBlue,
} }
}
}
export const getStaticProps = makeGetStaticProps(true)
but if you do this you will be saddened to see:
Module not found: Can't resolve 'fs'
So we understand another thing: the fs usage has to be directly inside the getStaticProps function body, Webpack can't catch it in subfunctions.
The only way to solve this is to have a separate file for the backend-only stuff as in:
pages/index.js
// Client + server code.
import { makeIndexPage } from "../front"
export default makeIndexPage(true)
// Server-only code.
import { makeGetStaticProps } from "../back"
export const getStaticProps = makeGetStaticProps(true)
pages/notindex.js
// Client + server code.
import { makeIndexPage } from "../front"
export default makeIndexPage(false)
// Server-only code.
import { makeGetStaticProps } from "../back"
export const getStaticProps = makeGetStaticProps(false)
front.js
// Client + server code.
import Link from 'next/link'
export function makeIndexPage(isIndex) {
return (props) => {
console.error('page');
return <>
<Link href={isIndex ? '/notindex' : '/'}>
<a>{isIndex ? 'notindex' : 'index'}</a>
</Link>
<div>{props.fs}</div>
<div>{props.isBlue}</div>
</>
}
}
back.js
// Server-only code.
const fs = require('fs')
export function makeGetStaticProps(isBlue) {
return () => {
return { props: {
fs: Object.keys(fs).join(' '),
isBlue,
} }
}
}
Webpack must see that name makeGetStaticProps getting assigned to getStaticProps, so it decides that the entire back file is server-only.
Note that it does not work if you try to merge back.js and front.js into a single file, probably because when you do export default makeIndexPage(true) webpack necessarily tries to pull the entire front.js file into the frontend, which includes the fs, so it fails.
This leads to a natural (and basically almost mandatory) split of library files between:
front.js and front/*: front-end + backend files. These are safe for the frontend. And the backend can do whatever the frontend can do (we are doing SSR right?) so those are also usable from the backend.
Perhaps this is the idea behind the conventional "components" folder in many official examples. But that is a bad name, because that folder should not only contain components, but also any library non-component helpers/constants that will be used from the frontend.
back.js and back/* (or alternatively anything outside of front/*): backend only files. These can only be used by the backend, importing them on frontend will lead to the error
fs,path or other node native modules can be used only inside server-side code, like "getServerSide" functions. If you try to use it in client you get error even you just console.log it.. That console.log should run inside server-side functions as well.
When you import "fs" and use it in server-side, next.js is clever enough to see that you use it in server-side so it wont add that import into the client bundle
One of the packages that I used was giving me this error, I fixed this with
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback.fs = false
}
return config
},
}
but this was throwing warning on terminal:
"Critical dependency: require function is used in a way in which
dependencies cannot be statically extracted"
Then I tried to load the node module on the browser. I copied the "min.js" of the node module from the node_modules and placed in "public/js/myPackage.js" and load it with Script
export default function BaseLayout({children}) {
return (
<>
<Script
// this in public folder
src="/js/myPackage.js"
// this means this script will be loaded first
strategy="beforeInteractive"
/>
</>
)
}
This package was attached to window object and in node_modules source code's index.js:
if (typeof window !== "undefined") {
window.TruffleContract = contract;
}
So I could access to this script as window.TruffleContract. BUt this was not an efficient way.
While this error requires a bit more reasoning than most errors you'll encounter, it happens for a straightforward reason.
Why this happens
Next.js, unlike many frameworks allows you to import server-only (Node.js APIs that don't work in a browser) code into your page files. When Next.js builds your project, it removes server only code from your client-side bundle by checking which code exists inside one any of the following built-in methods (code splitting):
getServerSideProps
getStaticProps
getStaticPaths
Side note: there is a demo app that visualizes how this works.
The Module not found: can't resolve 'xyz' error happens when you try to use server only code outside of these methods.
Error example 1 - basic
To reproduce this error, let's start with a working simple Next.js page file.
WORKING file
/** THIS FILE WORKS FINE! */
import type { GetServerSideProps } from "next";
import fs from "fs"; // our server-only import
type Props = {
doesFileExist: boolean;
};
export const getServerSideProps: GetServerSideProps = async () => {
const fileExists = fs.existsSync("/some-file");
return {
props: {
doesFileExist: fileExists,
},
};
};
const ExamplePage = ({ doesFileExist }: Props) => {
return <div>File exists?: {doesFileExist ? "Yes" : "No"}</div>;
};
export default ExamplePage;
Now, let's reproduce the error by moving our fs.existsSync method outside of getServerSideProps. The difference is subtle, but the code below will throw our dreaded Module not found error.
ERROR file
import type { GetServerSideProps } from "next";
import fs from "fs";
type Props = {
doesFileExist: boolean;
};
/** ERROR!! - Module not found: can't resolve 'fs' */
const fileExists = fs.existsSync("/some-file");
export const getServerSideProps: GetServerSideProps = async () => {
return {
props: {
doesFileExist: fileExists,
},
};
};
const ExamplePage = ({ doesFileExist }: Props) => {
return <div>File exists?: {doesFileExist ? "Yes" : "No"}</div>;
};
export default ExamplePage;
Error example 2 - realistic
The most common (and confusing) occurrence of this error happens when you are using modules that contain multiple types of code (client-side + server-side).
Let's say I have the following module called file-utils.ts:
import fs from 'fs'
// This code only works server-side
export function getFileExistence(filepath: string) {
return fs.existsSync(filepath)
}
// This code works fine on both the server AND the client
export function formatResult(fileExistsResult: boolean) {
return fileExistsResult ? 'Yes, file exists' : 'No, file does not exist'
}
In this module, we have one server-only method and one "shared" method that in theory should work client-side (but as we'll see, theory isn't perfect).
Now, let's try incorporating this into our Next.js page file.
/** ERROR!! */
import type { GetServerSideProps } from "next";
import { getFileExistence, formatResult } from './file-utils.ts'
type Props = {
doesFileExist: boolean;
};
export const getServerSideProps: GetServerSideProps = async () => {
return {
props: {
doesFileExist: getFileExistence('/some-file')
},
};
};
const ExamplePage = ({ doesFileExist }: Props) => {
// ERROR!!!
return <div>File exists?: {formatResult(doesFileExist)}</div>;
};
export default ExamplePage;
As you can see, we get an error here because when we attempt to use formatResult client-side, our module still has to import the server-side code.
To fix this, we need to split our modules up into two categories:
Server only
Shared code (client or server)
// file-utils.ts
import fs from 'fs'
// This code (and entire file) only works server-side
export function getFileExistence(filepath: string) {
return fs.existsSync(filepath)
}
// file-format-utils.ts
// This code works fine on both the server AND the client
export function formatResult(fileExistsResult: boolean) {
return fileExistsResult ? 'Yes, file exists' : 'No, file does not exist'
}
Now, we can create a WORKING page file:
/** WORKING! */
import type { GetServerSideProps } from "next";
import { getFileExistence } from './file-utils.ts' // server only
import { formatResult } from './file-format-utils.ts' // shared
type Props = {
doesFileExist: boolean;
};
export const getServerSideProps: GetServerSideProps = async () => {
return {
props: {
doesFileExist: getFileExistence('/some-file')
},
};
};
const ExamplePage = ({ doesFileExist }: Props) => {
return <div>File exists?: {formatResult(doesFileExist)}</div>;
};
export default ExamplePage;
Solutions
There are 2 ways to solve this:
The "correct" way
The "just get it working" way
The "Correct" way
The best way to solve this error is to make sure that you understand why it is happening (above) and make sure you are only using server-side code inside getStaticPaths, getStaticProps, or getServerSideProps and NOWHERE else.
And remember, if you import a module that contains both server-side and client-side code, you cannot use any of the imports from that module client-side (revisit example #2 above).
The "Just get it working" way
As others have suggested, you can alter your next.config.js to ignore certain modules at build-time. This means that when Next.js attempts to split your page file between server only and shared code, it will not try to polyfill Node.js APIs that fail to build client-side.
In this case, you just need:
/** next.config.js - with Webpack v5.x */
module.exports = {
... other settings ...
webpack: (config, { isServer }) => {
// If client-side, don't polyfill `fs`
if (!isServer) {
config.resolve.fallback = {
fs: false,
};
}
return config;
},
};
Drawbacks of this approach
As shown in the resolve.fallback section of the Webpack documentation, the primary reason for this config option is because as-of Webpack v5.x, core Node.js modules are no longer polyfilled by default. Therefore, the main purpose for this option is to provide a way for you to define which polyfill you want to use.
When you pass false as an option, this means, "do not include a polyfill".
While this works, it can be fragile and require ongoing maintenance to include any new modules that you introduce to your project. Unless you are converting an existing project / supporting legacy code, it is best to go for option #1 above as it promotes better module organization according to how Next.js actually splits the code under the hood.
If trying to use fs-extra in Next.js, this worked for me
module.exports = {
webpack: (config) => {
config.resolve.fallback = { fs: false, path: false, stream: false, constants: false };
return config;
}
}
I got this error in my NextJS app because I was missing export in
export function getStaticProps()
/** #type {import('next').NextConfig} */
module.exports = {
reactStrictMode: false,
webpack5: true,
webpack: (config) => {
config.resolve.fallback = {
fs: false,
net: false,
dns: false,
child_process: false,
tls: false,
};
return config;
},
};
This code fixed my problem and I want to share.Add this code to your next.config file.i'm using
webpack5
For me clearing the cache
npm cache clean -f
and then updating the node version to the latest stable release(14.17.0) worked
It might be that the module you are trying to implement is not supposed to run in a browser. I.e. it's server-side only.
For me, the problem was the old version of the node.js installed. It requires node.js version 14 and higher. The solution was to go to the node.js web page, download the latest version and just install it. And then re-run the project. All worked!
I had the same issue when I was trying to use babel.
For me this worked:
#add a .babelrc file to the root of the project and define presets and plugins
(in my case, I had some issues with the macros of babel, so I defined them)
{
"presets": ["next/babel"],
"plugins": ["macros"]
}
after that shut down your server and run it again
I had this exact issue. My problem was that I was importing types that I had declared in a types.d.ts file.
I was importing it like this, thanks to the autofill provided by VSCode.
import {CUSTOM_TYPE} from './types'
It should have been like this:
import {CUSTOM_TYPE} from './types.d'
In my case, I think the .d was unnecessary so I ended up removing it entirely and renamed my file to types.ts.
Weird enough, it was being imported directly into index.tsx without issues, but any helper files/functions inside the src directory would give me errors.
I ran into this in a NextJS application because I had defined a new helper function directly below getServerSideProps(), but had not yet called that function inside getServerSideProps().
I'm not sure why this created a problem, but it did. I could only get it to work by either calling that function, removing it, or commenting it out.
Don't use fs in the pages directory, since next.js suppose that files in pages directory are running in browser environment.
You could put the util file which uses fs to other directory such as /core
Then require the util in getStaticProps which runs in node.js environment.
// /pages/myPage/index.tsx
import View from './view';
export default View;
export async function getStaticProps() {
const util = require('core/some-util-uses-fs').default; // getStaticProps runs in nodes
const data = await util.getDataFromDisk();
return {
props: {
data,
},
};
}
In my case, this error appeared while refactoring the auth flow of a Next.js page. The cause was some an unused imports that I had not yet removed.
Previously I made the page a protected route like so:
export async function getServerSideProps ({ query, req, res }) {
const session = await unstable_getServerSession(req, res, authOptions)
if (!session) {
return {
redirect: {
destination: '/signin',
permanent: false,
},
}
}
//... rest of server-side logic
}
Whilst refactoring, I read up on NextAuth useSession. Based on what I read there, I was able to change the implementation such that I simply needed to add
MyComponent.auth = true to make a page protected. I then deleted the aforementioned code block inside of getServerSideProps. However, I had not yet deleted the two imports used by said code block:
import { unstable_getServerSession } from 'next-auth/next'
import { authOptions } from 'pages/api/auth/[...nextauth]'
I believe the second of those two imports was causing the problem. So the summary is that in addition to all of the great answers above, it could also be an unused import.
Sometimes this error can be because you have imported something but not mastered it anywhere. This worked for me. I reviewed my code and removed the unused dependencies.

How to fix "ReferenceError: Blob is not defined" in NextJs?

Hi I was trying to use react-qr-reader in next js but having the problem
Server Error
ReferenceError: Blob is not defined
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Source
external%20%22react-qr-reader%22 (1:0) # Object.react-qr-reader
> 1 | module.exports = require("react-qr-reader");
Call Stack
__webpack_require__
webpack\bootstrap (21:0)
How can I fix it?
This works for me:
const QrScan = dynamic(() => import('react-qr-reader'), { ssr: false })
The official docs says Server side rendering won't work for react-qr-reader. So you need to do is to avoid applying react-qr-reader in server-side. You can use the dynamic to solve the problem. You can also reference from the solution 2 of this solution to get some example code.
Great work guys, this works for me
import { useState } from "react";
import dynamic from "next/dynamic";
const QrReader = dynamic(() => import("react-qr-reader"), { ssr: false });
export default function ScanPage() {
const [state, setState] = useState("");
return (
<>
<div>{state}</div>
<QrReader delay={100}
onError={(err) => setState(err)}
onScan={(data) => setState(data)}
style={{ width: "95vw"}}
/>
</>
);}
I was having same issue on voice-recorder-react library. So after some work-arounds I was be able to solve it.
I just fixed by making the page csr (Client-Side-Rendering)
like this:
const HomePage = dynamic(() => import("./home"), { ssr: false });
And then I used the library as normal way of importing (in my home page).
import { useRecorder } from "voice-recorder-react";
So basically some of libraries need to be rendered on client-side.

Resources