vercel / next.js

The React Framework
https://nextjs.org
MIT License
124.67k stars 26.61k forks source link

renderToString is not available in the Instrumentation hook #67714

Open masiama opened 1 month ago

masiama commented 1 month ago

Link to the code that reproduces this issue

https://codesandbox.io/p/devbox/elastic-carson-np7v5v

To Reproduce

  1. Enable instrumentation hook
  2. Create a file with default export that renders component to string using react-dom/server
  3. Import and call this file in the instrumentation hook
  4. Start the server (next dev)

Current vs. Expected behavior

Current behavior

Starting...
 ⨯ ./parser.tsx
Error: 
  × You're importing a component that imports react-dom/server. To fix it, render or return the content directly as a Server Component instead for perf and security.
  │ Learn more: https://nextjs.org/docs/getting-started/react-essentials
   ╭─[/project/workspace/parser.tsx:1:1]
 1 │ import { renderToString } from "react-dom/server";
   · ──────────────────────────────────────────────────
 2 │ import Test from "./app/Test"
 3 │ 
 4 │ export default async function parse() {
   ╰────

Expected behavior

App launches successfully

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.5.0: Wed May  1 20:16:51 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T8103
Binaries:
  Node: 20.14.0
  npm: 10.8.2
  Yarn: 1.22.22
  pnpm: N/A
Relevant Packages:
  next: 14.1.4
  eslint-config-next: N/A
  react: 18.3.1
  react-dom: 18.3.1
  typescript: 5.5.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Instrumentation

Which stage(s) are affected? (Select all that apply)

next dev (local), next build (local)

Additional context

It works until Next 14.2.0. Next <=14.1.4 works

icyJoseph commented 1 month ago

May I ask, what do you do with the rendered string from instrumentation? Output to some HTML document?

masiama commented 1 month ago

TL;DR We have a website with a search feature. We use an instrumentation hook to "parse" all pages' components and save them to DB as a page index.


We have the company's website. It has static landings. We also have a search feature on the website. To search all landings, we need some page index (list of words from the page). We use an instrumentation hook to prepare an index for all landings. We go through all landings' components, parse them to string, and extract words to save to DB.

How it works:

  1. Every page has a Body that contains all sections of the landing. It is both rendered on the page and exported for the instrumentation hook to have access. It is used to not render the entire page with header, footer, etc.
  2. Outside of the pages folder we have a file that exports an array of objects for each page. That object contains the Body.
  3. From the instrumentation hook we call the function that goes through the array mentioned above, parses each landing's content, and saves it to DB
feedthejim commented 1 month ago

Why not run that as a script outside of Next.js before you instantiate the server? This is not the intended purpose of the instrumentation hook.

masiama commented 1 month ago

We used an instrumentation hook instead of just a script outside Next.js because we render components that import styles (*.module.scss). I tried ts-node and tsx to run the same script we put in the instrumentation hook – it can't load styles:

/src/components/BlogPost/BlogPost.module.scss:1
@use '@/styles/vars.scss';
^

SyntaxError: Invalid or unexpected token
    at wrapSafe (node:internal/modules/cjs/loader:1281:20)
    at Module._compile (node:internal/modules/cjs/loader:1321:27)
    at Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
    at Object.transformer (/node_modules/tsx/dist/register-CCR7NebB.cjs:3:868)
    at Module.load (node:internal/modules/cjs/loader:1208:32)
    at Module._load (node:internal/modules/cjs/loader:1024:12)
    at Module.require (node:internal/modules/cjs/loader:1233:19)
    at require (node:internal/modules/helpers:179:18)
    at blogPostStyles (/src/utils/index.tsx:4:28)
    at Object.<anonymous> (/src/utils/index.tsx:109:17)

Node.js v20.14.0
LZL0 commented 1 month ago

I don't understand why this was intentionally removed. I have a use case for this. Imagine a big JSON object as the article content and generating a JSX to render later. After generating said JSX I still need to render it into an HTML to have things like an excerpt, word count of the article, some teaser content, minutes it takes to read it, and more. The whole thing is handled neatly inside multiple dedicated classes.

Can we just get renderToString back? The current behavior makes NO sense.