resend / react-email

💌 Build and send emails using React
https://react.email
MIT License
13.9k stars 628 forks source link

All builds failing with react-email templates on Next.js 14 with error "Cannot get final name for export 'encodeXML'" #1499

Open WoetDev opened 3 months ago

WoetDev commented 3 months ago

Describe the Bug

I'm having a blocking issue for all my builds with Next.js 14.2.3:

Cannot get final name for export 'encodeXML' of ./react-email/node_modules/.pnpm/entities@4.5.0/node_modules/entities/lib/esm/index.js

It seems that this is already a long-standing issue that has been closed without proper resolution: https://github.com/resend/react-email/issues/1015

The mentioned workarounds do also not work for me:

  1. Tried to use the render function to first render the react component to HTML and send the email
  2. Tried to add the following to my next.config.js:
    experimental: {
    serverComponentsExternalPackages: ["@react-email/components"],
    },

Not using the Edge runtime on my page

  1. Tried setting my page as a client component with "use client" at the top

Used packages:

"@react-email/components": "^0.0.19", "react-email": "^2.1.4",

Which package is affected (leave empty if unsure)

No response

Link to the code that reproduces this issue

private

To Reproduce

Create a next 14 project and use the following packages:

"@react-email/components": "0.0.19",
"react-email": "2.1.4",

Expected Behavior

Build succeeds

What's your node version? (if relevant)

v20.13.1

WoetDev commented 3 months ago

Defaulted back to send the email by plain text for now so my build succeeds.

If the issue keeps being unresolved, I'll probably have no option than to look into an alternative on how to handle the HTML templating and with which service. Which is a shame cause the development experience was great until it came to now having it work on production.

gabrielmfern commented 2 months ago

Can you make an actual reproduction of this? Something I can clone and get up and running quickly because the original issue there was fixed, and it isn't present anymore. Your issue seems to be something else.

WoetDev commented 2 months ago

@gabrielmfern thanks for getting back to me. I'll see if I can find time to create smth to reproduce the issue. Although I think it might also be something from the resend package and I could be in the wrong place here.

Cause I currently managed to get around it by sending the HTML email from an edge API route instead of sending the email directly from my server action on the edge runtime, so in the server action I only send a POST request to the API route to trigger the email send and this worked.

maldee commented 1 month ago

add transpilePackages and serverComponentsExternalPackages to your next.config.js

`/* @type {import('next').NextConfig} / const nextConfig = {

images: { remotePatterns: [ //////////////////// ], }, transpilePackages: ['html-to-text'], experimental: { serverComponentsExternalPackages: ["@react-email/components"], }, }

module.exports = nextConfig`

If your get ESLint rule violation, update your email template body section Example: you're free

<div > you&apos;re free </div>

WoetDev commented 1 month ago

@maldee still the same error unfortunately when trying to call it directly from the server action:

image

shennguyenrs commented 1 month ago

Hi, I got the same problems as yours. My project was used Next Js 14.1.3 and react-email 2.1.5. I have tried to reproduce this bug on blank Next JS (latest + my version) projects. But It did not happen. The blank project could be built without touching next.config.mjs file, this solution, and even passing React component as react param in resend.email.send configuration that running in server action and as api route. I applied these solutions into my project and all could not make it build success.

Finally, I came with this solution. Rather than using renderAsync I use render instead to render html output at transactional directory, in your case I think It could be react-email. Then at the server action in parent project, I imported the html instead of react email component.

// At my-app/transactional directory 
// render.js
import { render } from "@react-email/render";
import WaitListEmail from "./emails/WaitListEmail"

export default function renderWaitList(props) {
  return render(WaitListEmail(props), {
      pretty: true,
    });
 }
// At server action inside my-app/src/actions
// sendEmailAction.js
import renderWaitList from ".../../transactional/render"

const { error } = await resend.emails.send({
  from,
  to,
  subject,
  html: renderWaitList(props),
});

Hope that you could make a build success with this solution.

gabrielmfern commented 1 month ago

In what situation does the error happen exactly? I can see this is an issue people have, but I can't reproduce at all.

This also might be a more general NextJS issue https://github.com/vercel/next.js/issues/60807, and if it is, it should be fixed under their canaries.

kylekz commented 1 month ago

I've been logging my findings in Discord but I'll leave them here for others too.

I've stripped my project down to the bare minimum here: https://github.com/kylekz/email-repro

At this point, I'm almost certain it's something to do with instantiating a new instance of Resend(), as seen on this line https://github.com/kylekz/email-repro/blob/main/apps/web/src/app/actions.ts#L8

// actions.ts
"use server";
import { RemovedFromOrganization } from "@repo/email/emails/removed-from-org";
import { Resend } from "resend";

// const resend = new Resend("");
// uncommenting this line fixes the error

export async function email() {
  const react = RemovedFromOrganization({
    baseUrl: "",
    receiverName: "",
    organizationName: "",
  });
}
// page.tsx
"use client";
export default function MyComponent() {
  async function send() {
    startTransition(async () => {
      await email();
    });
  }

  return <button onClick={send}>Send Email</button>;
}

In my project, I have Notification classes for each of my emails which handle persisting to the database for web notifications, dispatching to a queue worker for background email sending, and marshalling from JSON into classes when receiving from the queue worker, so where I'm returning the React component template is not necessarily where the email is being sent.

If I place an unused const resend = new Resend("env.RESEND_API_KEY"); inside the file where my notification classes are defined, I'm able to work with email templates via resend.emails.send({ react: EmailTemplate({ ... }) }), without using @react-email/render beforehand. This makes no sense to me, and I'm unable to see anything in the resend-node package that could indicate why this is the case.

gabrielmfern commented 1 month ago

Thank you so much for making the repro @kylekz, helped out a lot.

After experimenting for a bit on it, this can't be an issue on our side, as it seems really volatile about when it happens. Just importing resend, as mentioned, fixes the issue, but also, importing each component from their package instead of from @react-email/components also fixes the issue.

The package it points to — html-to-text — is a dependency of @react-email/render, but importing render directly anywhere does not cause the same error when building. After testing this on the latest canary for next´the issue goes away in any of the cases I mentioned, therefore seems to me that https://github.com/vercel/next.js/issues/60807 fixes this.

If anyone wants to upgrade to Next 15's canaries, you also need to upgrade your React and React Email dependencies accordingly. So, upgrade your React Email dependencies to the canaries we have, which also support React 19 directly.