2

I've a project with (next 12.1.6 + react 18.1 + react-dom 18.1). Now i want to add React Server Components to it. Updated my next.config.js like this

const nextConfig = {
  distDir: '../../.next',
  reactStrictMode: true,
  experimental: {
    runtime: 'nodejs',
    serverComponents: true,
    concurrentFeatures: true,
  },
};

module.exports = nextConfig;

But regardless of that, when i execute npm run start:dev (npm run build works fine i believe) i get the error:

Error: `experimental.runtime` requires `experimental.reactRoot` to be enabled along with React 18. at Object.getBaseWebpackConfig [as default] (G:\ORLVNIcard\orlvni-business-card\node_modules\next\build\webpack-config.ts:355:11) at async Promise.all (index 0) at Span.traceAsyncFn (G:\ORLVNIcard\orlvni-business-card\node_modules\next\trace\trace.ts:106:14) at Span.traceAsyncFn (G:\ORLVNIcard\orlvni-business-card\node_modules\next\trace\trace.ts:106:14) at HotReloader.start (G:\ORLVNIcard\orlvni-business-card\node_modules\next\server\dev\hot-reloader.ts:518:21) at DevServer.prepare (G:\ORLVNIcard\orlvni-business-card\node_modules\next\server\dev\next-dev-server.ts:404:5) at ViewService.onModuleInit (G:\ORLVNIcard\orlvni-business-card\src\server\view\view.service.ts:19:7) at async Promise.all (index 0) at Object.callModuleInitHook (G:\ORLVNIcard\orlvni-business-card\node_modules\@nestjs\core\hooks\on-module-init.hook.js:43:5) at NestApplication.callInitHook (G:\ORLVNIcard\orlvni-business-card\node_modules\@nestjs\core\nest-application-context.js:169:13)

Next.js documentation says in https://nextjs.org/docs/advanced-features/react-18/server-components that i should use next@canary instead of next@12.1.6, and it works, but i have another problem with @canary in HookWebpackError: The "to" argument must be of type string. Received undefined and @12.1.6 does not throw the error in fresh barely empty project with (next 12.1.6 + react 18.1 + react-dom 18.1).

Part of my package.json looks like this

"scripts": {
    "prebuild": "rimraf dist",
    "build": "nest build && cd src/client && next build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "typeorm": "node --require ts-node/register ./node_modules/typeorm/cli.js",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },
  "dependencies": {
    "@nestjs/common": "^8.0.0",
    "@nestjs/config": "^1.1.5",
    "@nestjs/core": "^8.0.0",
    "@nestjs/jwt": "^8.0.0",
    "@nestjs/mongoose": "^9.0.2",
    "@nestjs/passport": "^8.0.1",
    "@nestjs/platform-express": "^8.0.0",
    "@types/bcrypt": "^5.0.0",
    "@types/mongoose": "^5.11.97",
    "bcrypt": "^5.0.1",
    "cookie-parser": "^1.4.6",
    "dotenv": "^10.0.0",
    "mongodb": "^4.3.1",
    "mongoose": "^6.1.10",
    "mssql": "^7.3.0",
    "next": "^12.1.6",
    "passport": "^0.4.1",
    "passport-jwt": "^4.0.0",
    "react": "18.1.0",
    "react-dom": "18.1.0",
    "reflect-metadata": "^0.1.13",
    "rimraf": "^3.0.2",
    "rxjs": "^7.2.0",
    "sass": "^1.45.0",
    "ts-node": "^10.0.0"
  },
  "devDependencies": {
    "@nestjs/cli": "^8.0.0",
    "@nestjs/schematics": "^8.0.0",
    "@nestjs/testing": "^8.0.0",
    "@types/cookie-parser": "^1.4.2",
    "@types/express": "^4.17.13",
    "@types/jest": "27.0.2",
    "@types/node": "^16.0.0",
    "@types/passport-jwt": "^3.0.6",
    "@types/react": "^18.0.12",
    "@types/react-dom": "^18.0.5",
    "@types/supertest": "^2.0.11",
    "@typescript-eslint/eslint-plugin": "^5.0.0",
    "@typescript-eslint/parser": "^5.0.0",
    "eslint": "^7.28.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-prettier": "^4.0.0",
    "jest": "^27.2.5",
    "prettier": "^2.3.2",
    "source-map-support": "^0.5.20",
    "supertest": "^6.1.3",
    "ts-jest": "^27.0.3",
    "ts-loader": "^9.2.3",
    "ts-node": "^10.0.0",
    "tsconfig-paths": "^3.10.1",
    "typescript": "^4.3.5"
  },

And this is how i start my next app

import { Injectable, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import createServer from 'next';
import { NextServer } from 'next/dist/server/next';
import { Request, Response } from 'express';

@Injectable()
export class ViewService implements OnModuleInit {
  private server: NextServer;

  constructor(private configService: ConfigService) {}

  async onModuleInit(): Promise<void> {
    try {
      this.server = createServer({
        dev: this.configService.get<string>('NODE_ENV') !== 'production',
        dir: './src/client',
      });
      await this.server.prepare();
    } catch (error) {
      console.error(error);
    }
  }

  handler(req: Request, res: Response) {
    return this.server.getRequestHandler()(req, res);
  }
}

Guess the error is due to next somehow not seeing react 18 in @12.1.6, but i can't realize why, especially while in new project everything works fine.

ORLVNI
  • 31
  • 1
  • 8
  • Have you tried simply adding `reactRoot: true` to the `experimental` object in `next.config.js`? – juliomalves Jun 11 '22 at 15:51
  • @juliomalves yes, as i remember i was getting "you need to have react 18 to use experimental reactRoot" error or something like this. btw i've found the solution already, thx anyway – ORLVNI Jun 14 '22 at 13:16

3 Answers3

1

Remove your package-lock.json and install the canary version of next

npm install next@canary react@latest react-dom@latest

Then add expermiental settings in your next.config.js

 experimental: { 
      reactRoot: true, 
      runtime: "nodejs", 
      serverComponents: true, 
      concurrentFeatures: true 
  }
1

Apparently the error was thrown 'cause by default createServer() method of Next.js is creating react server with react 17 and old methods of react, like render(), hydrate() and so on. To make createServer() create react 18 server it's necessary to define __NEXT_REACT_ROOT as true in the environment of the process of our custom server. At least i've understood it this way. So the solution is

async onModuleInit(): Promise<void> {
    process.env.__NEXT_REACT_ROOT = 'true';
    try {
      this.server = createServer({
        dev: this.configService.get<string>('NODE_ENV') !== 'production',
        dir: './src/client',
      });
      await this.server.prepare();
    } catch (error) {
      console.error(error);
    }
  }

It solved the problem for me.

ORLVNI
  • 31
  • 1
  • 8
0

Just add this line to your .env file and it will fix the issue

__NEXT_REACT_ROOT=true
M.R.Safari
  • 1,857
  • 3
  • 30
  • 47