Html-react-parser: TS Error : instanceof Element always return false with Next.js

Created on 29 Jan 2021  ·  15Comments  ·  Source: remarkablemark/html-react-parser

Reproducible Demo

https://codesandbox.io/s/quizzical-lake-16q4o?file=/pages/index.tsx

When replacing to a custom component, false will always be returned.

I am currently using the any type.
However, is there any other solution other than the any type?

question

Most helpful comment

I following this instruction, and I also have same problem, I can't get property attribs from domNode.

Because typescript check domNode is instance of DOMNode, not Element type, so that the error occurs, because DOMNode hasn't attribs property.

And if you check with this condition console.log(domNode instanceof Element) the return always false, because instance of domNode is DOMNode.

I have solution for this, maybe can help. I defined the Element type at the parameter domNode.

import parse, { HTMLReactParserOptions } from "html-react-parser";
import { Element } from "domhandler/lib/node";

export function contentHandler(postContent: string) {
  const options: HTMLReactParserOptions = {
    replace: (domNode: Element) => {
      if (domNode.attribs) {
        if (domNode.attribs.id === 'shortcode') {
          return <div className="leadform">Shortcode</div>;
        }
      }
    },
  };

  return parse(postContent, options);
}

All 15 comments

Thanks for opening the issue @purp1eeeee

In your CodeSandbox example, you're not returning a React element so replace won't work. See readme.

So can you replace the logs with a return of your custom element?

export default function IndexPage() {
   const html = "<h1>hoge</h1>";
   const options: HTMLReactParserOptions = {
     replace: (domNode) => {
-      console.log(domNode);
-      console.log(domNode instanceof Element);
+      if (domNode.name === "h1") {
+        return <h1>replaced</h1>;
+      }
     }
   };
   return <div>{parse(html, options)}</div>;
 }

See updated CodeSandbox.

I following this instruction, and I also have same problem, I can't get property attribs from domNode.

Because typescript check domNode is instance of DOMNode, not Element type, so that the error occurs, because DOMNode hasn't attribs property.

And if you check with this condition console.log(domNode instanceof Element) the return always false, because instance of domNode is DOMNode.

I have solution for this, maybe can help. I defined the Element type at the parameter domNode.

import parse, { HTMLReactParserOptions } from "html-react-parser";
import { Element } from "domhandler/lib/node";

export function contentHandler(postContent: string) {
  const options: HTMLReactParserOptions = {
    replace: (domNode: Element) => {
      if (domNode.attribs) {
        if (domNode.attribs.id === 'shortcode') {
          return <div className="leadform">Shortcode</div>;
        }
      }
    },
  };

  return parse(postContent, options);
}

I can confirm I'm having the same issue with a Next.js project; Typescript type-casting domNode seems to be the only work around after upgrading html-react-parser.

@kakaeriel
so good! thank you👍

Having these issues with a Typescript project. Like @kakaeriel I'm following this instruction about replace including the section on Typescript.

The solution from @kakaeriel works for me. The instanceof operator doesn't work (Element is not a type) and after hours browsing through node_modules looking for a type definition, finding this workaround is a life-saver.

Thanks for providing an alternative solution @kakaeriel. Would you be interested in creating a PR to improve the README.md documentation?

@remarkablemark Yes sure! I will create a PR, ASAP.

Wow. I have the same issue using NextJS.

Because typescript check domNode is instance of DOMNode, not Element type, so that the error occurs, because DOMNode hasn't attribs property.

```

So why does this only occur in a NextJS app.
I setup a react-scripts typescript app and there it works.
https://codesandbox.io/s/html-parsing-and-replacing-elements-yjtv7?file=/src/index.tsx

I'm not sure @Naxos84. I bet Next.js has a different TypeScript configuration. Please check out @kakaeriel's solution for a workaround.

@kakaeriel let me know if you're able to open a PR to update the README.md with your workaround. If you're unable to do it, I can do it. Thanks!

I don't think that it is NextJs related.
I tried a little more and voila it worked somehow.
I changed @remarkablemark codesandbox and added console.log(domElement.constructor) as mentioned on Stack Overflow.

The result looks as follows:
grafik

So I don't understand why it is suddenly working!?

Though it is not working in "my" nextjs project.
Update: And now it's not working again....

I suggest using a custom typeguard.

const isElement = (domNode: DOMNode): domNode is Element => {
    const isTag = domNode.type === "tag";
    const hasAttributes = (domNode as Element).attribs !== undefined;

    return isTag && hasAttributes;
};
const parserOptions: HTMLReactParserOptions = {
    replace: domNode => {
        if (isElement(domNode)) {
            return <p>It's an element.</p>;
        } else {
            return <p>It's something else</p>;
        }
    },
};

Hey everyone, I was able to fix this in Next.js 10.x with this solution. It's a public Gist where you can find my current implementation details. The solution has zero type errors in my setup.

@natterstefan Thanks for sharing your solution!

I have the same problem with NextJS. Element is always undefined. I typed domNode as any and converted the options type as HTMLReactParserOptions. Element from domhandler doesn't work with SSR?

const options = {
  replace: (domNode: any) => {
      // ...
  }
} as HTMLReactParserOptions;

I was able to fix this with the code below, hope it would help

Screen Shot 2021-06-12 at 20 32 31

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alizeaiter picture alizeaiter  ·  3Comments

lhtdesignde picture lhtdesignde  ·  9Comments

mdeljavan picture mdeljavan  ·  4Comments

thany picture thany  ·  7Comments

christianfredh picture christianfredh  ·  5Comments