react-email icon indicating copy to clipboard operation
react-email copied to clipboard

'Unexpected closing tag "td". Help!

Open jimapl opened this issue 1 year ago β€’ 11 comments

Describe the Bug

After bumping react and nextjs to latest, I get this error on all my email renders:

import {render} from "@react-email/render";
...
export const customVerificationRequest = async ({token, identifier: email} : {
    token: string;
    identifier: string;
}) => {
    try {

        const html = await render(<MagicSigninEmail token={token}/>, {
            pretty: true,
        });
       ...

    } catch (e) {
        console.error({e})
    }
}

Which throws

[cause]: t {
  span: [h],
  msg: 'Unexpected closing tag "td". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags',
  level: 1,
  elementName: 'td'
}
    "@react-email/components": "^0.0.28",
    "@react-email/render": "^1.0.3",
     "react-email": "3.0.4",

Which package is affected (leave empty if unsure)

No response

Link to the code that reproduces this issue

Private repo

To Reproduce

Switching to stable branch everything works. Meaning react 18 / nextjs 14. Have not changed any code, both render and renderAsync this happens.

Expected Behavior

Email to be rendered

What's your node version? (if relevant)

v20.10.0

jimapl avatar Dec 24 '24 15:12 jimapl

We can't reproduce your issue if you don't share a reproduction for it. What was the content for the email template that generates the error? If we were to guess, it would be almost impossible and a waste of time.

Going to close this issue as not planned, since it is basically unsolvable without the proper instructions. Share the reproduction and I might reopen if it is satisfactory.

gabrielmfern avatar Dec 24 '24 16:12 gabrielmfern

It’s a straight copy from the react email templates. Will paste it here later when home. Brb Same code does not break in react 18

Will get back

jimapl avatar Dec 24 '24 17:12 jimapl

The email template in question:

https://gist.github.com/jim-spr/c3668ec9dc36277e63e744e9fb33565f

it's pretty much from the react-email templates. Again, I have not changed any code. I bumped next to latest and react version to 19. Anything else I should provide?

@gabrielmfern

Edit:

even sending an empty column. All other components commented out, it breaks. When I comment column out, it sends successfully!

jim-spr avatar Dec 31 '24 11:12 jim-spr

Managed to solve this issue by deleting the Column component. Is this deprecated?

jim-spr avatar Dec 31 '24 11:12 jim-spr

@gabrielmfern I'm seeing this issue, too, with @react-email/[email protected] and above. It appears to be an issue with render expecting tag omission. This:

await render(
   <p>
      <div>Hi</div>
   </p>,
   { pretty: true }
);

fails, while this:

await render(
   <p>
      <span>Hi</span>
   </p>,
   { pretty: true }
);

works without error.

EleanorLee42 avatar Jan 13 '25 19:01 EleanorLee42

@gabrielmfern gabrielmfern

jimmailcamp avatar Jan 15 '25 09:01 jimmailcamp

@gabrielmfern if it helps, I'm wondering if this change from js-beautify to prettier is what caused this issue. I've noticed that the render only fails when { pretty: true } is passed.

EleanorLee42 avatar Jan 15 '25 14:01 EleanorLee42

Sorry for the delay, looking into this right now!

gabrielmfern avatar Jan 15 '25 15:01 gabrielmfern

as @EleanorLee42 mentioned, this is an issue happening with the way we currently prettify the output from render, so as a workaround you can simply disable the pretty option for now

gabrielmfern avatar Jan 15 '25 15:01 gabrielmfern

The error itself seems to point to this in the HTML standard

13.2.6.3 Closing elements that have implied end tags When the steps below require the UA to generate implied end tags, then, while the current node is a dd element, a dt element, an li element, an optgroup element, an option element, a p element, an rb element, an rp element, an rt element, or an rtc element, the UA must pop the current node off the stack of open elements.

If a step requires the UA to generate implied end tags but lists an element to exclude from the process, then the UA must perform the above steps as if that element was not in the above list.

When the steps below require the UA to generate all implied end tags thoroughly, then, while the current node is a caption element, a colgroup element, a dd element, a dt element, an li element, an optgroup element, an option element, a p element, an rb element, an rp element, an rt element, an rtc element, a tbody element, a td element, a tfoot element, a th element, a thead element, or a tr element, the UA must pop the current node off the stack of open elements.

gabrielmfern avatar Jan 15 '25 15:01 gabrielmfern

React doesn't disallow users to write down any invalid HTML, like having divs inside paragraphs, but prettier's HTML formatter seem to be very strict in this sense.

It goes without saying, though, that you should not write HTML with divs inside paragraphs, or tds inside tds, since once this HTML gets to the browser they will be rendered differently by following the definitions from the spec and will lead to unexpected behavior.

As for the solution on this, I am not quite 100% sure but will keep you all updated. An idea is to still error, but have something clearer.

gabrielmfern avatar Jan 21 '25 19:01 gabrielmfern

I am pretty sure that prettifying html should not break rendering, no matter what kind of content you have.

I also getting this error and, for instance, my html is 100% correct and eslint have no errors reported. But I still get Unexpected closing tag "p" error when enable pretty: true.

huksley avatar Apr 13 '25 12:04 huksley

Closing this as complete because we have improved the error messages for this from [email protected] and the versions afterwards!

gabrielmfern avatar Jul 18 '25 14:07 gabrielmfern