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

Objects are not valid as a React child Error when sending react-email with resend #1803

Open
wtkw opened this issue Dec 6, 2024 · 18 comments
Labels
Type: Bug Confirmed bug

Comments

@wtkw
Copy link

wtkw commented Dec 6, 2024

Describe the Bug

Sending an email with resend using react component throws a following error:
[Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, key, ref, props, _owner, _store}). If you meant to render a collection of children, use an array instead.]

It used to work in the past, but some updates (react-email, resend or react) must have broken it.

If sent as a HTML string, it works.

Which package is affected (leave empty if unsure)

react-email

Link to the code that reproduces this issue

private repo

To Reproduce

Sending an email with resend using react component:

await resend.emails.send({
      to: email,
      from: 'Page <[email protected]>',
      subject: 'Verify your account',
      react: VerifyLinkEmail({
        token,
        url,
      }),
    })

throws a following error:
[Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, key, ref, props, _owner, _store}). If you meant to render a collection of children, use an array instead.]

The E-Mail is this:

const VerifyLinkEmail = ({ token, url }: VerifyLinkEmailProps) => {
  return (
    <Html>
      <Head />
      <Preview>Confirm the e-mail to register.</Preview>
      <Tailwind>
        <Body className="my-12 bg-white font-sans">
          <Container className="mx-auto w-[400px] border px-4 py-10 text-center shadow-md">
            <Section className="my-12">
              <Img
                src="https://link/logo_plain.png"
                className="mx-auto h-16 w-16"
              />
              <Heading as="h1" className="text-3xl text-blue-600">
                Page Name
              </Heading>
            </Section>
            <Text>
              To finish the registration process, click the button below
            </Text>
            <Button
              href={`${url}/auth/verify?token=${token}`}
              className="w-24 rounded-md bg-blue-600 px-4 py-3 font-mono tracking-widest text-white"
            >
              VERIFY
            </Button>
            <Text>Link is only valid for one hour</Text>
            <Section className="mt-6">
              <Text className="text-xs">
                If you didn&apos;t try to login, you can safely ignore this
                email.
              </Text>
            </Section>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  )
}

package.json:

   "dependencies": {
    "@react-email/components": "^0.0.29",
    "next": "15.0.3",
    "react": "18.3.1",
    "react-dom": "18.3.1",
    "resend": "^4.0.1",
    "typescript": "5.6.3",
  }

If sent as a HTML string, it works:

await resend.emails.send({
      to: email,
      from: 'Page <[email protected]>',
      subject: 'Verify your account',
      html: '<p>Test</p>'
    })

Expected Behavior

Email sent without an error.

What's your node version? (if relevant)

22.11.0

@wtkw wtkw added the Type: Bug Confirmed bug label Dec 6, 2024
@Shriom-Trivedi
Copy link

Hi @wtkw do you have a setup for the code anywhere to check?

@Shriom-Trivedi
Copy link

Hi @wtkw

It worked when I passed the component like this <Component /> instead of Component.

So maybe you can try this.

await resend.emails.send({
      to: email,
      from: 'Page <[email protected]>',
      subject: 'Verify your account',
      react: <VerifyLinkEmail token={token} url={url}/>,
    })

@wtkw
Copy link
Author

wtkw commented Dec 7, 2024

Despite to what the documentation says I can only pass it as a function. There is no way I can import it as a Component I think because it is a part of the NextJS POST API function which runs on a server.

Hi @wtkw do you have a setup for the code anywhere to check?

I have it on my private repo, as a part of the authentication route. I could reproduce parts of it on public repo somehow (both backend, frontend, prisma, etc.) but before that I can maybe try to give some more context if this would help?

@wtkw
Copy link
Author

wtkw commented Dec 7, 2024

As mentioned, it is a POST function on NextJS API sign-up route, which gets user's registration data from a form in the frontend:

import { NextResponse } from 'next/server'
import prisma from '@/src/lib/prisma'
import bcrypt from 'bcrypt'
import { resend } from '@/src/lib/resend'
import VerifyLinkEmail from '@/src/lib/email-templates/VerificationEmail'
import jwt, { Secret } from 'jsonwebtoken'
import { getBaseUrl } from '@/src/utils/utils'

export async function POST(req: Request) {
  const body = await req.json()
  const { name, email, password, confirmPassword } = body

  // * Check if user exists and validate input data
  try {
    const existingUser = await prisma.user.findUnique({
      where: {
        email,
      },
    })

    if (existingUser) {
      return NextResponse.json(
        {
          message:
            'User with this e-mail already exists. Did you forget, you have an account?',
        },
        { status: 422 },
      )
    }

    if (password !== confirmPassword) {
      return NextResponse.json(
        { message: 'Repeated password incorrect.' },
        { status: 422 },
      )
    }
  } catch (error) {
    console.log('[AUTH_SIGNUP_POST]', error)
    return NextResponse.json(
      { message: 'Internal authentication error. Try again later.' },
      { status: 500 },
    )
  }

  // * Hash verification code and send verification email
  try {
    const token = "generatedToken"
    const url = getBaseUrl()

    await resend.emails.send({
      to: email,
      from: 'Page <[email protected]>',
      subject: 'Verify your account',
      react: VerifyLinkEmail({
         token,
         url,
      }),
    })
  } catch (error) {
    console.log('[VERIFICATION_EMAIL]', error)
    return NextResponse.json(
      { message: 'Verification e-mail not sent. Try again later.' },
      { status: 500 },
    )
  }

  // * Create new user
  try {
    const hashedPassword = await bcrypt.hash(body.password, 12)

    const newUser = await prisma.user.create({
      data: {
        email,
        hashedPassword,
        name,
        emailVerified: null,
      },
    })

    return NextResponse.json(newUser)
  } catch (error) {
    console.log('[AUTH_SIGNUP_POST]', error)
    return NextResponse.json(
      { message: 'Verification e-mail not sent. Try again later.' },
      { status: 500 },
    )
  }
}

after sending the email with a verification token, user is created in the DB to verify and activate the account.

It used to work flawlessly until something got updated (either react or nextjs or resend) and I did not catch it on time with teesting...

@dBianchii
Copy link

happening with me on react-email package:

Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, key, props, _owner, _store}). If you meant to render a collection of children, use an array instead.

image

@Shriom-Trivedi
Copy link

@wtkw interesting... could you also tell me how you are using the component and its setup. Would love to see what is going on in here.

@Shriom-Trivedi
Copy link

@dBianchii could you also share the usecase that you are using?

@dBianchii
Copy link

dBianchii commented Dec 9, 2024

@dBianchii could you also share the usecase that you are using?

Sure. I am just using the react-email package so I can preview my emails. I think sending emails on my backend is working fine

BTW, I am on React19

import { Body, Head, Html, Preview, Tailwind } from "@react-email/components";

export default function WarnDelayedCriticalTasks({
  taskTitle = "Comer macarrão",
  // taskDate = new Date(),
}: {
  taskTitle: string | null;
  // taskDate: Date;
}) {
  return (
    <Html>
      <Head />
      <Preview>{`A tarefa ${taskTitle} está atrasada`}</Preview>
      <Tailwind>
        <Body className="mx-auto my-auto bg-white font-sans">
         Just testing
        </Body>
      </Tailwind>
    </Html>
  );
}

@gabrielmfern
Copy link
Collaborator

gabrielmfern commented Dec 9, 2024

@dBianchii that issue seems to be unrelated, but it does mean that there are multiple React versions running at the same time in your case because the element structure changed from React 18 to 19

@wtkw
Copy link
Author

wtkw commented Dec 10, 2024

@wtkw interesting... could you also tell me how you are using the component and its setup. Would love to see what is going on in here.

The user register form triggers an onSubmit function:

const onSubmit: SubmitHandler<Inputs> = async (values) => {
    try {
      setIsLoading(true)
      await axios.post('/api/auth/signup', values)
      toast({
        variant: 'default',
        title: 'User registered successfully',
        description: 'Check your email to verify your account',
        duration: 9000,
      })
      router.push(`/auth/verify?email=${values.email}`)
    } catch (error: unknown) {
      if (error instanceof AxiosError) {
        toast({
          variant: 'destructive',
          title: 'Error ' + error.response?.status,
          description: error.response?.data?.message,
          duration: 9000,
        })
      } 
    } finally {
      form.reset()
      setIsLoading(false)
    }
  }

which uses axios (it was written before Server Actions in NextJS became a thing) to send the POST request I pasted above. POST request triggers a serverside function to send an email with resend.

The e-mail is not send correctly due to above mentioned error, so the error response is presented back on the frontend.

Please let me know what other exact information would you need?

@emroot
Copy link

emroot commented Dec 11, 2024

I have this workaround on my end.

import { render } from '@react-email/components';
....
  const content = await render(MyComponent(props));

  await resend.emails.send({
    from: EmailFrom,
    to: emails,
    subject,
    html: content,
  });

Keep getting the error in this format:

import { render } from '@react-email/components';
....
  await resend.emails.send({
    from: EmailFrom,
    to: emails,
    subject,
    react: MyComponent(props),
  });

@hexcowboy
Copy link

hexcowboy commented Dec 12, 2024

I also am seeing this error on the dev server with React 19. Pretty basic repro steps here:

{
  "name": "email",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "email dev --port 3030"
  },
  "devDependencies": {
    "react-email": "^3.0.4"
  },
  "dependencies": {
    "@react-email/components": "^0.0.31",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  }
}
import { Link, Section, Text, Button } from "@react-email/components";
import React from "react";

const InviteEmail = () => (
<Html>
  <Body
    <Text style={{ marginBottom: "24px" }}>
      Someone invited you to join Example.
      To get started, open the{" "}
      <Link
        href="https://example.com/dashboard"
        target="_blank"
      >
        Example dashboard
      </Link>{" "}
      and log in:
    </Text>

    <Section style={{ marginTop: "12px", marginBottom: "32px" }}>
      <Button href="https://example.com/login">Log in to Example.com</Button>
    </Section>

    <Text className="mb-4 text-gray-800">
      If you have any questions, feel free to{" "}
      <Link
        href="mailto:[email protected]"
        style={{
          textDecoration: "underline",
        }}
      >
        contact us
      </Link>{" "}
      anytime.
    </Text>
  </Body>
</Html>
);

export default InviteEmail;

@wtkw
Copy link
Author

wtkw commented Dec 12, 2024

I have this workaround on my end.

Tried that but the render function throws following:

[VERIFICATION_EMAIL] Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, key, props, _owner, _store}). If you meant to render a collection of children, use an array instead.
    at renderNodeDestructiveImpl (C:\Users\User\Projects\projects\page\node_modules\.pnpm\[email protected][email protected]\node_modules\react-dom\cjs\react-dom-server.node.development.js:6256:11)
    at renderNodeDestructive (C:\Users\User\Projects\projects\page\node_modules\.pnpm\[email protected][email protected]\node_modules\react-dom\cjs\react-dom-server.node.development.js:6153:14)
    at renderNode (C:\Users\User\Projects\projects\page\node_modules\.pnpm\[email protected][email protected]\node_modules\react-dom\cjs\react-dom-server.node.development.js:6336:12)
    at renderSuspenseBoundary (C:\Users\User\Projects\projects\page\node_modules\.pnpm\[email protected][email protected]\node_modules\react-dom\cjs\react-dom-server.node.development.js:5657:5)
    at renderElement (C:\Users\User\Projects\projects\page\node_modules\.pnpm\[email protected][email protected]\node_modules\react-dom\cjs\react-dom-server.node.development.js:6071:11)
    at renderNodeDestructiveImpl (C:\Users\User\Projects\projects\page\node_modules\.pnpm\[email protected][email protected]\node_modules\react-dom\cjs\react-dom-server.node.development.js:6181:11)
    at renderNodeDestructive (C:\Users\User\Projects\projects\page\node_modules\.pnpm\[email protected][email protected]\node_modules\react-dom\cjs\react-dom-server.node.development.js:6153:14)
    at retryTask (C:\Users\User\Projects\projects\page\node_modules\.pnpm\[email protected][email protected]\node_modules\react-dom\cjs\react-dom-server.node.development.js:6605:5)
    at performWork (C:\Users\User\Projects\projects\page\node_modules\.pnpm\[email protected][email protected]\node_modules\react-dom\cjs\react-dom-server.node.development.js:6653:7)
    at Immediate.<anonymous> (C:\Users\User\Projects\projects\page\node_modules\.pnpm\[email protected][email protected]\node_modules\react-dom\cjs\react-dom-server.node.development.js:6980:12)

@tecoad
Copy link

tecoad commented Dec 12, 2024

I have the same issue.

@rrcabrera9625
Copy link

I just created a Stack Overflow question for that here... Im having the same issue with Nodemailer, so I don't think it's related to resend...

@rrcabrera9625
Copy link

Hello Folks!

I just got it working. I simply switched back to using renderAsync (instead of render) with the
react-email/[email protected] version. It seems the "new" render has some issue that I haven't been able to identify yet.

Here's a working code example:

import { renderAsync } from 'npm:@react-email/[email protected]'
import { ReservationConfirmEmail } from './_templates/reservation-code.tsx'

// ...

const html = await renderAsync(React.createElement(ReservationConfirmEmail))

// ... Send html via Nodemailer

Result: Email sent successfully with the template rendered correctly.

@gabrielmfern
Copy link
Collaborator

Can anyone here make a minimal reproduction of this? With or without the Resend SDK?

@zackperdue
Copy link

I think the issue is with conflicting @babel/core. If you run npm list @babel/core, you'll see something like this:

@remix-run/[email protected]
│   ├─┬ @babel/[email protected] invalid: "7.24.5" from node_modules/react-email

What other react projects do you guys have in your monorepo?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Bug Confirmed bug
Projects
None yet
Development

No branches or pull requests

9 participants