Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next.js 15 Circular Structure Error When passing domNode to another server component #1589

Open
webplantmedia opened this issue Nov 1, 2024 · 17 comments
Assignees
Labels
question Further information is requested

Comments

@webplantmedia
Copy link

webplantmedia commented Nov 1, 2024

I'm not sure if this is a bug, or some problem with my NextJS environment, but in Next 14, I could pass domNode from within the replace() function to other server components, and keep my react customizations more organized. But after upgrading to Next 15, I'm getting the following error:

[ Server ] Error: Converting circular structure to JSON
    --> starting at object with constructor 'Element'
    |     property 'prev' -> object with constructor 'Element'
    --- property 'next' closes the circle

Is this a bug?

  const options: HTMLReactParserOptions = {
    replace(domNode) {
      if (domNode instanceof Element && domNode.attribs) {
        if (domNode.name === 'ul') {
          return <CustomList node={domNode} />;
				}
			}
		}
	}
@webplantmedia webplantmedia added the question Further information is requested label Nov 1, 2024
@webplantmedia webplantmedia changed the title Circular Structure When passing domNode to another server component in Next 15 Circular Structure Error When passing domNode to another server component in Next 15 Nov 1, 2024
@remarkablemark
Copy link
Owner

Can you provide a reproducible example @webplantmedia? Is this happening on the server-side or client-side? Just curious: if you render with 'use client', does the same error appear?

@webplantmedia
Copy link
Author

webplantmedia commented Nov 1, 2024

Happening all on the server side. I read in HTML from an API. I parse the content like so:

  const html = parse(content, options);

My options are defined similar to the code here:

const options: HTMLReactParserOptions = {
    replace(domNode) {
      if (domNode instanceof Element && domNode.attribs) {
        if (domNode.name === 'ul') {
          return <CustomList node={domNode} />;
				}
			}
		}
	}

Next 14, no error. Next 15, Circular Structure Error.

@remarkablemark
Copy link
Owner

Gotcha I'll try to reproduce this in the example Next.js app in this repository when I have some time today

@remarkablemark
Copy link
Owner

remarkablemark commented Nov 1, 2024

I upgraded the example Next.js app to v15 and I wasn't able to reproduce your error: https://github.com/remarkablemark/html-react-parser/tree/master/examples/nextjs

Let me know if there's anything I can adjust to reproduce your error

@remarkablemark
Copy link
Owner

remarkablemark commented Nov 1, 2024

My suspicion is that it's coming from this line:

<CustomList node={domNode} />

Do you call JSON.stringify on domNode in <CustomList>?

@webplantmedia
Copy link
Author

webplantmedia commented Nov 1, 2024

Even this is producing the error for me:

  const options: HTMLReactParserOptions = {
    replace(domNode) {
      function test(node: any) {
        console.log(node);
      }
      test(domNode);
    },
  };
  const html = parse(content, options);

  return <>{html}</>;

Error output:

Error: Converting circular structure to JSON
    --> starting at object with constructor 'Element'
    |     property 'next' -> object with constructor 'Element'
    --- property 'prev' closes the circle
    at test (rsc://React/Server/webpack-internal:///(rsc)/./components/blocks/parse-blocks.tsx?2:17:25)
    at Object.replace (rsc://React/Server/webpack-internal:///(rsc)/./components/blocks/parse-blocks.tsx?3:19:13)
    at ParseBlocks (rsc://React/Server/webpack-internal:///(rsc)/./components/blocks/parse-blocks.tsx?4:22:79)
    at resolveErrorDev (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js:1792:63)
    at processFullStringRow (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js:2071:17)
    at processFullBinaryRow (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js:2059:7)
    at progress (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js:2262:17)
image

@remarkablemark
Copy link
Owner

Do you get the same error if you do this in the example Next.js app? https://github.com/remarkablemark/html-react-parser/tree/master/examples/nextjs

@webplantmedia
Copy link
Author

webplantmedia commented Nov 1, 2024

Yes, I do get the same error when in the app directory. No error if in the pages directory. Here is my code in the app directory.

layout.tsx

export const metadata = {
  title: 'Next.js',
  description: 'Generated by Next.js',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

page.tsx

import parse, { Element } from 'html-react-parser';

type Props = {
  params: { slug: string };
};

export default async function Page({ params }: Props) {
  return (
    <main>
      <h1 className="title">
        {parse(
          `
            Welcome to <a href="https://nextjs.org">Next.js</a>
            and HTMLReactParser!
          `,
          {
            replace(domNode) {
              function test(node: any) {
                console.log(node);
              }
              test(domNode);

              if (domNode instanceof Element && domNode.name === 'a') {
                return (
                  <a href="https://nextjs.org" rel="noopener noreferrer">
                    Next.js
                  </a>
                );
              }
            },
          }
        )}
      </h1>
    </main>
  );
}

Error:

Error: Converting circular structure to JSON
    --> starting at object with constructor 'Text'
    |     property 'next' -> object with constructor 'Element'
    --- property 'prev' closes the circle
    at test (rsc://React/Server/webpack-internal:///(rsc)/./app/page.tsx?0:20:33)
    at Object.replace (rsc://React/Server/webpack-internal:///(rsc)/./app/page.tsx?1:22:21)
    at Page (rsc://React/Server/webpack-internal:///(rsc)/./app/page.tsx?2:14:84)
    at resolveErrorDev (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js:1792:63)
    at processFullStringRow (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js:2071:17)
    at processFullBinaryRow (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js:2059:7)
    at progress (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js:2262:17)
image

package.json

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "html-react-parser": "^5.1.18",
    "next": "^15.0.2",
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },
  "devDependencies": {
    "@types/node": "22.8.6",
    "@types/react": "18.3.12",
    "typescript": "5.6.3"
  }
}

@remarkablemark
Copy link
Owner

Can you fork and create a branch with these changes? @webplantmedia

@webplantmedia
Copy link
Author

webplantmedia commented Nov 1, 2024

https://github.com/webplantmedia/html-react-parser

I just pushed a commit with the code. Thanks so much for looking into it! I have based a very big next js project in using this react parser. I'm hoping there is an easy fix to this without having to refactor lots of code.

https://github.com/webplantmedia/html-react-parser/tree/master/examples/nextjs

@remarkablemark
Copy link
Owner

Thanks! I'll take a look later today @webplantmedia

@webplantmedia
Copy link
Author

Any insight on the nature of this error? Is it some new code procedure introduced in next js 15? If it is permanent, would I need to drop support of this parser if I need to stick with Next JS? Thank you again. I've been stuck on this for two days.

@remarkablemark
Copy link
Owner

remarkablemark commented Nov 1, 2024

@webplantmedia this is happening because Next.js tries to render the data as JSON in a <script> tag:

Screenshot 2024-11-01 at 4 53 07 PM

There are 3 solutions:

  1. Add 'use client' to the top of the file <CustomList> but you will lose server-side rendering
  2. Instead of passing domNode to the function, pass the specific keys and values
  3. Remove circular references from domNode: https://stackoverflow.com/questions/66091604/how-do-i-remove-all-circular-references-from-an-object-in-javascript

@webplantmedia
Copy link
Author

webplantmedia commented Nov 1, 2024

Sigh. I really needed to be able to loop inside other components. And keep it as a server component. Do you see this being a permanent move by nextjs 15 on how they handle complex objects?

I really appreciate your insight. Thanks so much.

@remarkablemark
Copy link
Owner

@webplantmedia I'm not sure, but I think it's worth creating an issue or discussion on Next.js to confirm. For now, I think 3 could resolve the issue for you, but there may be a chance that it breaks your project.

@remarkablemark remarkablemark pinned this issue Nov 1, 2024
@remarkablemark remarkablemark changed the title Circular Structure Error When passing domNode to another server component in Next 15 Next.js 15 Circular Structure Error When passing domNode to another server component Nov 1, 2024
@webplantmedia
Copy link
Author

I did!

vercel/next.js#72189

I'm assuming if I remove circular reference, it would break domToReact(node?.children as DOMNode[], options) in other components. Let me know if that's not the case. Also, seems like a lot of extra recursion too for large html content.

But I can't even console log domNode if I want to debug in a server component.

Two other html parsers I looked into also have circular referencing.

Sigh.

@remarkablemark
Copy link
Owner

@webplantmedia yes there's a possibility that if you remove circular reference that it would break domToReact(). This library is built using html-dom-parser, which uses htmlparser2 under the hood. That's where the circular references are coming from.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants