react-email: renderToReadableStream not found in react-dom/server

Describe the Bug

We are testing out react-email for use in our project, but getting this error when trying to deploying a test template:

yarn start
yarn run v1.22.19
$ serverless offline --httpPort 4051 --stage local --host 0.0.0.0
serverless-offline-ssm checking serverless version 3.33.0.
✖ in ../node_modules/@react-email/components/node_modules/@react-email/render/dist/index.mjs 31:23-45
    export 'renderToReadableStream' (imported as 'renderToReadableStream') was not found in 'react-dom/server' (possible exports: renderToNodeStream, renderToPipeableStream, renderToStaticMarkup, renderToStaticNodeStream, renderToString, version)

Template code:

import React from 'react'
import {
  Body,
  Head,
  Heading,
  Container,
  Html,
  Text,
} from '@react-email/components'

interface AccessRequestParams {
  testName: string
}

export const accessRequestExpiryTemplate = ({
  testName,
}: AccessRequestParams) => (
  <Html>
    <Head />
    <Body>
      <Container>
        <Heading>TEST</Heading>
        <Text>Here are the details of the request:</Text>
        <Text>{testName}</Text>
      </Container>
    </Body>
  </Html>
)

export default accessRequestExpiryTemplate

Package.json versions

    "dependencies": {
    "@react-email/components": "^0.0.7",
    "react-email": "^1.9.4",
    "react": "18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.11.2",
    "react-tooltip": "4.2.21",
  }

Which package is affected (leave empty if unsure)

@react-email/components

Link to the code that reproduces this issue

n/a

To Reproduce

Deploy with the listed package versions - We’re using serverless: ‘“start”: “serverless offline --httpPort 4051 --stage local --host 0.0.0.0”’

Expected Behavior

The code should compile cleanly without this import error

What’s your node version? (if relevant)

18.13.0

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 14
  • Comments: 63 (7 by maintainers)

Commits related to this issue

Most upvoted comments

I’ve been using this happily with Next.js 13.5.6 but it fails immediately on upgrade to Next.js 14.0.0.

Can confirm this completely breaks on Next 14.0.0

We’re currently working on a fix for that. Will keep you all posted.

Confirming that react email worked fine until upgrading to next 14.

For me this issue pops up with the latest Next.js release v13.5.6, which bumped the version of React it uses

Yup this breaks on upgrade to Next 14 🙃

@catalinpit I was able to resolve the issue as well. Thank you. Below is the method I applied:

  1. Navigate to the .react-email directory and then delete the package-lock.json and yarn.lock files.
  2. Modify @react-email/render in package.json from "0.0.7" to "0.0.9".
  3. Run yarn

If anyone is looking for a temporary NextJS 14 fix (using @Moicky’s solution)

in next.config.js add these imports:

const path = require("path");
const {
    NormalModuleReplacementPlugin
} = require("webpack");

& add this webpack configuration somewhere in the file:

    webpack: (
        config, {
            buildId,
            dev,
            isServer,
            defaultLoaders,
            nextRuntime,
            webpack
        }
    ) => {
        config.plugins = config.plugins || []
        config.plugins.push(new NormalModuleReplacementPlugin(
            /email\/render/,
            path.resolve(__dirname, "./renderEmailFix.js"),
        ))
        // Important: return the modified config
        return config
    }

Finally, create a new file renderEmailFix.js in the root folder with this code:

// @ts-nocheck
const {
    renderToStaticMarkup
} = require("react-dom/server");

module.exports.render = (component) => {
    const doctype =
        '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
    const markup = renderToStaticMarkup(component);
    return `${doctype}${markup}`;
};

@subvertallchris no prob. fwiw we’ve completely moved away from using react for rendering, which makes it fully compatible with edge and all fe frameworks. using react for rendering was a big footgun

My experience with https://github.com/shellscape/jsx-email has been good so far. It’s easier to set up the preview server and bugs get fixed quickly if you offer a reproduction.

Also unable to get working with NextJS 14

We just released a new canary version @react-email/render@0.0.9-canary.2.

here’s an example on Next.js 14 app router edge

import { NextResponse } from 'next/server';
import { EmailTemplate } from '../../../components/email-template';
import { renderAsync } from '@react-email/render';

export const runtime = 'edge';

export async function GET() {
  try {
    const html = await renderAsync(
      EmailTemplate({ firstName: 'John' }) as React.ReactElement
    );
    return NextResponse.json({ html });
  } catch (error) {
    return NextResponse.json({ error });
  }
}

Okay, the solution was simpler than expected.

Even though I updated package.json with the appropriate version, the package-lock.json contained the older versions, and the app somehow picked those (something something about the node module resolution algorithm).

To fix this, I added the line “@react-email/render": "0.0.9” both under the “dependencies” and “overrides” in the root package.json. It now forces all references to that package to use v0.0.9

Snap

Many thanks to @lxunos for the fix/lesson. 🙏

This actually worked, thanks! Delete package-lock and node_modules, npm i, and it’s working.

Okay, the solution was simpler than expected.

Even though I updated package.json with the appropriate version, the package-lock.json contained the older versions, and the app somehow picked those (something something about the node module resolution algorithm).

To fix this, I added the line “@react-email/render": "0.0.9” both under the “dependencies” and “overrides” in the root package.json. It now forces all references to that package to use v0.0.9

Snap

Many thanks to @lxunos for the fix/lesson. 🙏

We just released a new canary version @react-email/render@0.0.9-canary.2.

here’s an example on Next.js 14 app router edge

import { NextResponse } from 'next/server';
import { EmailTemplate } from '../../../components/email-template';
import { renderAsync } from '@react-email/render';

export const runtime = 'edge';

export async function GET() {
  try {
    const html = await renderAsync(
      EmailTemplate({ firstName: 'John' }) as React.ReactElement
    );
    return NextResponse.json({ html });
  } catch (error) {
    return NextResponse.json({ error });
  }
}

Working for me…appreciate the fast work

So I have updated to "resend": "^2.0.0-canary.0" and this issue is resolved.

Issue => https://github.com/resendlabs/resend-node/issues/244

@bukinoshita also still getting build time error. Tried @react-email/render 0.0.9-canary.0 and 0.0.9-canary.1 with "@react-email/components": "0.0.9" and "react-email": "1.9.5",.

./node_modules/resend/node_modules/@react-email/render/dist/index.mjs
Attempted import error: 'renderToReadableStream' is not exported from 'react-dom/server' (imported as 'renderToReadableStream').

@bukinoshita I still get it with

"@react-email/render": "0.0.9-canary.1",
TypeError: readableStream.getReader is not a function
    at eval (webpack-internal:///(rsc)/./node_modules/.pnpm/@react-email+render@0.0.9-canary.1/node_modules/@react-email/render/dist/index.mjs:63:39)
    at Generator.next (<anonymous>)
    at eval (webpack-internal:///(rsc)/./node_modules/.pnpm/@react-email+render@0.0.9-canary.1/node_modules/@react-email/render/dist/index.mjs:30:65)
    at new Promise (<anonymous>)
    at __async (webpack-internal:///(rsc)/./node_modules/.pnpm/@react-email+render@0.0.9-canary.1/node_modules/@react-email/render/dist/index.mjs:14:12)
    at readReactDOMServerReadableStream (webpack-internal:///(rsc)/./node_modules/.pnpm/@react-email+render@0.0.9-canary.1/node_modules/@react-email/render/dist/index.mjs:62:58)
    at eval (webpack-internal:///(rsc)/./node_modules/.pnpm/@react-email+render@0.0.9-canary.1/node_modules/@react-email/render/dist/index.mjs:81:16)
    at Generator.next (<anonymous>)
    at eval (webpack-internal:///(rsc)/./node_modules/.pnpm/@react-email+render@0.0.9-canary.1/node_modules/@react-email/render/dist/index.mjs:30:65)
    at new Promise (<anonymous>)
    at __async (webpack-internal:///(rsc)/./node_modules/.pnpm/@react-email+render@0.0.9-canary.1/node_modules/@react-email/render/dist/index.mjs:14:12)
    at readStream (webpack-internal:///(rsc)/./node_modules/.pnpm/@react-email+render@0.0.9-canary.1/node_modules/@react-email/render/dist/index.mjs:75:36)
    at eval (webpack-internal:///(rsc)/./node_modules/.pnpm/@react-email+render@0.0.9-canary.1/node_modules/@react-email/render/dist/index.mjs:92:28)
    at Generator.next (<anonymous>)
    at fulfilled (webpack-internal:///(rsc)/./node_modules/.pnpm/@react-email+render@0.0.9-canary.1/node_modules/@react-email/render/dist/index.mjs:17:32)

We’re currently working on a fix for that. Will keep you all posted.

The new release doesn’t work because in package.json appears workspace:*

https://www.npmjs.com/package/@react-email/components?activeTab=code

Following, exited to get rid of experimental in next.config.js I updated to 14 and now get ./node_modules/@react-email/render/dist/index.mjs Attempted import error: ‘renderToReadableStream’ is not exported from ‘react-dom/server’ (imported as ‘renderToReadableStream’).

Hey Guys, Those who use the Resend library, update to V2.0.0, it works in Next14. https://www.npmjs.com/package/resend

I also moved to jsx-email. Way better and is not breaking. Migration took 10 minutes.

As @bukinoshita mentioned, the fix is in @react-email/render@0.0.9

We’ll just need to upgrade your local @react-email/render package from “0.0.7” to “0.0.9”.

  • In ./react-starter-email/package.json, pin the @react-email/render version to 0.0.9 using resolutions and also bump the dep versions for good measure:
// ./react-starter-email/package.json
{
  "dependencies": {
    "@react-email/components": "0.0.11",
    "react-email": "1.9.5"
  },
  "resolutions": {
    "@react-email/render": "^0.0.9"
  }
}
  1. Do npm/yarn/pnpm install to apply the change.
  2. Done.

Thanks @shellscape , I moved over to jsx-emails and it worked straight out of the box. And migration (for my very small use case) was painless too

The patch file I posted yesterday is also working fine for me.

On Fri, Oct 27, 2023 at 7:43 AM themendelson @.***> wrote:

If anyone is looking for a temporary NextJS 14 fix -

in next.config.js add these imports:

const path = require(“path”); const { NormalModuleReplacementPlugin } = require(“webpack”);

& add this webpack configuration somewhere in the file:

webpack: (
    config, {
        buildId,
        dev,
        isServer,
        defaultLoaders,
        nextRuntime,
        webpack
    }
) => {
    config.plugins = config.plugins || []
    config.plugins.push(new NormalModuleReplacementPlugin(
        /email\/render/,
        path.resolve(__dirname, "./renderEmailFix.js"),
    ))
    // Important: return the modified config
    return config
}

Finally, create a new file renderEmailFix.js in the root folder with this code:

// @ts-nocheck const { renderToStaticMarkup } = require(“react-dom/server”);

module.exports.render = (component) => { const doctype = ‘<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">’; const markup = renderToStaticMarkup(component); return ${doctype}${markup}; };

— Reply to this email directly, view it on GitHub https://github.com/resendlabs/react-email/issues/868#issuecomment-1782771917, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA7IJ53PGXWRVS2V5WLVRATYBOM4RAVCNFSM6AAAAAA2Z5JI52VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTOOBSG43TCOJRG4 . You are receiving this because you were mentioned.Message ID: @.***>

So now there are no patch/fix solution for react-email with Next 14 ?

Here’s what the fix for this looks like shellscape/jsx-email@2eeebfc

Thank you so much @shellscape! Here is a patch file that implements this for @react-email/render

diff --git a/node_modules/@react-email/render/dist/index.mjs b/node_modules/@react-email/render/dist/index.mjs
index 27cc390..99fd935 100644
--- a/node_modules/@react-email/render/dist/index.mjs
+++ b/node_modules/@react-email/render/dist/index.mjs
@@ -26,9 +26,19 @@ var renderAsPlainText = (component, _options) => {
 // src/renderAsync.ts
 import { convert as convert2 } from "html-to-text";
 import pretty2 from "pretty";
-import { renderToReadableStream, renderToStaticMarkup as renderToStaticMarkup2 } from "react-dom/server";
+
+import react from 'react-dom/server';
+
+const { renderToStaticMarkup, renderToStaticMarkup: rendertoStaticMarkup2 } = react;
+const renderToStream =
+  // Note: only available in platforms that support WebStreams
+  // https://react.dev/reference/react-dom/server/renderToString#alternatives
+  react.renderToReadableStream ||
+  // Note: only available in Node
+  react.renderToPipeableStream;
+
 async function renderToString(children) {
-  const stream = await renderToReadableStream(children);
+  const stream = await renderToStream(children);
   const html = await readableStreamToString(
     stream
   );

It worked for me

    "@react-email/components": "^0.0.11",
    "@react-email/render": "0.0.9-canary.2",

my nextjs version: “next”: “14.0.4”,

It works for me, Thank you.

It worked for me

    "@react-email/components": "^0.0.11",
    "@react-email/render": "0.0.9-canary.2",

my nextjs version: “next”: “14.0.4”,

+1 for migrating to jsx-email. Painless and worked out of the box in our nx monorepo.

Can you try using the canary version of render to see if this is still happening?

{
  ...,
  "@react-email/render": "0.0.9-canary.0",
}

Maybe this helps someone:

I needed a quick fix since my webpack kept crashing due to this function not being available in my NodeJS environment.

I used NormalModuleReplacementPlugin to provide a fake implementation since this function is not used in my app:

/* webpack.config.js */

const path = require("path");
const { NormalModuleReplacementPlugin } = require("webpack");

module.exports = {
  // ...
  plugins: [
    new NormalModuleReplacementPlugin(
      /email\/render/,
      path.resolve(__dirname, "./src/renderEmailFix.js"),
    ),
  ],
  // ...
};
// @ts-nocheck
const { renderToStaticMarkup } = require("react-dom/server");

module.exports.render = (component) => {
  const doctype =
    '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
  const markup = renderToStaticMarkup(component);
  return `${doctype}${markup}`;
};

No luck with any of the above packages. What magic are you all using? 😂 Also on next14, with overrides and all.

render

If you’re still having issues with renderToReadableStream on @react-email/render, you should upgrade the @react-email/render to v0.0.9 (latest).

Eg:

import { renderAsync } from '@react-email/render'

const html = async renderAsync(EmailTemplate({ firstName: 'John' }))

components

If you’re still having issues with renderToReadableStream on @react-email/components, you should upgrade the @react-email/components to v0.0.11 (latest).

Eg:

import { renderAsync } from '@react-email/components'

const html = async renderAsync(EmailTemplate({ firstName: 'John' }))

resend

If you’re still having issues with renderToReadableStream on resend, you should upgrade the resend to v2.0.0 (latest).

Eg:

const { data, error } = await resend.emails.send({
  from: 'team@example.com',
  to: 'delivered@resend.dev',
  subject: 'Hello!',
  react: EmailTemplate({ firstName: 'John' })
})

Still having issues after upgrading

  • If you’re using render or components, please provide a code example and your setup (a reproducible repository works better)
  • If you’re using resend, please follow up in the resend-node repository

None of the options above worked for me.

I might have found a solution though: installing components separately, rather than using @react-email/components. So I have them in my package.json like this now:

    "@react-email/body": "^0.0.4",
    "@react-email/button": "^0.0.11",
    "@react-email/container": "^0.0.10",
    "@react-email/head": "^0.0.6",
    "@react-email/html": "^0.0.6",
    "@react-email/preview": "^0.0.7",
    "@react-email/section": "^0.0.10",
    "@react-email/tailwind": "^0.0.12",
    "@react-email/text": "^0.0.6",

Everything works great and builds with no errors, even with the latest version of resend (2.0.0). I am using Next 14.0.1. Hope that helps someone! I spent way too much time trying to fix react-email and resend related errors lately

@alerimoficial We have a Resend version that upgrades the react-email version once the issue is fully fixed

I tried to upgrade to 0.0.9-canary.1 but looks like the resend still depends on 0.0.7 @react-email/render version.

I’m wondering what’s your setup, if one can confirm how are you using? I’m testing this way in next.js 14

import { render } from '@react-email/render';
import { MyEmail } from '../../../emails/my-email';
import { NextResponse } from 'next/server';

export const runtime = 'edge'

export const GET = async () => {
  const html = await render(MyEmail());
  return NextResponse.json({ html }, { status: 200 })
}

@subvertallchris no prob. fwiw we’ve completely moved away from using react for rendering, which makes it fully compatible with edge and all fe frameworks. using react for rendering was a big footgun