tiernacity
tiernacity11mo ago

Problem integrating react-email into an internal action

Hi, I'm trying to send email from a convex action, using https://resend.com/ and https://github.com/resend/react-email. It's a next.js 13 app. The resend integration is working, it's just a simple call to their SDK. But when I try to integrate react-email, the convex functions won't deploy. My action (in /convex/email.ts) imports and calls a function from another module (in /convex/src/email/new_share.ts). This module then imports a react-email component from a .tsx file outside of /convex (in /emails/new_share.tsx). This component needs to be provided to the resend SDK. When I run npx convex dev -v --once, I can see .js files generated from my two files inside of /convex. I can also see .css files generated for each - these are presumably needed by react-email. I haven't yet investigated whether react-email can build without them. Regardless, my convex dev command fails because it doesn't want to upload CSS files. I've tried adding 'use node' in the action file, but see the same error. Any advice or guidance you can give me? Here's some edited output that shows the error:
➜ npx convex dev -v --once
Codegen finished.
...
Preparing convex/email.ts
Preparing convex/src/email/new_share.ts
...
Convex's runtime modules: [
...
'src/email/new_share.js',
'email.js',
...
'src/email/new_share.css',
'email.css'
]
Node.js runtime modules: []
Remote config will be overwritten with the following changes:
...
Add the following modules:
[+] src/email/new_share.js (327 B, source map 93 B)
[+] email.js (631 B, source map 903 B)
...
[+] src/email/new_share.css (1.6 KB, source map 2.0 KB)
[+] email.css (1.6 KB, source map 2.0 KB)

✖ Error: Unable to push deployment config to https://<redacted>.convex.cloud
400 Bad Request: BadConvexModuleIdentifier: Hit an error while pushing:
src/email/new_share.css is not a valid path to a Convex module. Module path (src/email/new_share.css) has an extension that isn't 'js'.
➜ npx convex dev -v --once
Codegen finished.
...
Preparing convex/email.ts
Preparing convex/src/email/new_share.ts
...
Convex's runtime modules: [
...
'src/email/new_share.js',
'email.js',
...
'src/email/new_share.css',
'email.css'
]
Node.js runtime modules: []
Remote config will be overwritten with the following changes:
...
Add the following modules:
[+] src/email/new_share.js (327 B, source map 93 B)
[+] email.js (631 B, source map 903 B)
...
[+] src/email/new_share.css (1.6 KB, source map 2.0 KB)
[+] email.css (1.6 KB, source map 2.0 KB)

✖ Error: Unable to push deployment config to https://<redacted>.convex.cloud
400 Bad Request: BadConvexModuleIdentifier: Hit an error while pushing:
src/email/new_share.css is not a valid path to a Convex module. Module path (src/email/new_share.css) has an extension that isn't 'js'.
30 Replies
erquhart
erquhart11mo ago
Modules have to be js, I would look into whether react-email can precompile somehow. I'm honestly curious how this works at all, they must be assuming you're using a bundler?
erquhart
erquhart11mo ago
Ah, looks like you'll want to use this: https://react.email/docs/utilities/render
React Email
Render - React Email
Transform React components into HTML email templates.
tiernacity
tiernacityOP11mo ago
Yes, I tried yesterday but no beans. I've just noticed that the .css files disappear if I don't use their tailwind integration - which is progress. Perhaps that + the render utility
erquhart
erquhart11mo ago
Looking through the docs, it doesn't look like they have a great path for Resend + react-email except for Next.js.
tiernacity
tiernacityOP11mo ago
With no tailwind, convex still doesn't like the TSX
400 Bad Request: InvalidModules: Hit an error while pushing:
Loading the pushed modules encountered the following
error:
Failed to analyze src/email/new_share.js: Uncaught SyntaxError: Unexpected token '<'
400 Bad Request: InvalidModules: Hit an error while pushing:
Loading the pushed modules encountered the following
error:
Failed to analyze src/email/new_share.js: Uncaught SyntaxError: Unexpected token '<'
Yes and I guess my fallback option is to punt this into a next.js API route - then call that from convex
erquhart
erquhart11mo ago
I would probably reach for that as well, honestly.
ballingt
ballingt11mo ago
@tiernacity here's a Resend + react-email example https://github.com/thomasballinger/convex-html-email-example — but it doesn't look like this will help with the css issue
GitHub
GitHub - thomasballinger/convex-html-email-example
Contribute to thomasballinger/convex-html-email-example development by creating an account on GitHub.
tiernacity
tiernacityOP11mo ago
This doesn't help (enough) I still get an error
400 Bad Request: InvalidModules: Hit an error while pushing:
Loading the pushed modules encountered the following
error:
Failed to analyze
src/email/new_share.js: Uncaught SyntaxError: Unexpected token '<'
400 Bad Request: InvalidModules: Hit an error while pushing:
Loading the pushed modules encountered the following
error:
Failed to analyze
src/email/new_share.js: Uncaught SyntaxError: Unexpected token '<'
ballingt
ballingt11mo ago
What's the file extension of that file? new_share.js
tiernacity
tiernacityOP11mo ago
Ahh thanks! I'll take a look. Looks like it's their tailwind support that adds the CSS - so I can drop that at least
ballingt
ballingt11mo ago
we have esbuild set up to need .tsx/.jsx to allow angle bracket jsx syntax
tiernacity
tiernacityOP11mo ago
I'm not sure from the output. Probably Sorry, yes it's .ts
ballingt
ballingt11mo ago
If this is something you can share I'd love to see, or if you can point me at the guide you're following. We should need to support this a bit better and make a test changing to .tsx may help that's your template or helper file, yeah?
tiernacity
tiernacityOP11mo ago
I have an file with the action /convex/email.ts. A file with a module that imports the react-email SDK and the email template /convex/emails/new_share.ts. And the template itself /emails/new_share.tsx I've just tried changing all these file extensions to .tsx but I'm still seeing an Unexpected token error. Let me take a longer look at the repo/example you shared, see if I can get me head around that
ballingt
ballingt11mo ago
there's a tsconfig setting, looks like I set "jsx": "react" in the tsconfig.json the one in the convex directory
tiernacity
tiernacityOP11mo ago
I'm following this to install react-email (https://react.email/docs/getting-started/manual-setup) and this to integrate w/ resend (https://react.email/docs/integrations/resend)
React Email
Manual Setup - React Email
Create a brand-new folder with packages powered by React Email.
React Email
Send email using Resend - React Email
Learn how to send an email using React Email and the Resend Node.js SDK.
ballingt
ballingt11mo ago
Great, thanks curious if changing that tsconfig.json setting changes this
tiernacity
tiernacityOP11mo ago
Ok, thanks guys! A combination of: - adopting your tsconfig.json - moving my email templates from /emails to /convex/emails means that my convex function has deployed now. Unfortunately I can't test it because resend is down today - but it looks promising and I'm unblocked. Shame that the CSS files prevent me from using react-email's tailwind integation - but hey, I can live without that
erquhart
erquhart11mo ago
Woah they've had a rough couple days
tiernacity
tiernacityOP11mo ago
Yeah, 13 hours and counting. Someone is having a really bad day 😦 Follow-up: resend is back up and my react-email has sent from a convex action. Thanks for the support and guidance
oferitz
oferitz10mo ago
@ballingt I'm revisiting the issue because I'm still struggling to make this work. I have followed this post and another one at https://discord.com/channels/1019350475847499849/1161498699558559754. Additionally, I have set up a simple example similar to the one you linked from GitHub. I have created my own tsconfig.json in the convex folder, just like in your example. It seems to do the trick regarding building TSX files, but now I am encountering TypeScript errors in my function. For example, in this function:
export const createTemplate = authMutation({
args: {
name: v.string(),
},
handler: async ({ db, user }, args) => {
const { name } = args
return await db.insert('templates', {
userId: user._id,
name
})
}
})
export const createTemplate = authMutation({
args: {
name: v.string(),
},
handler: async ({ db, user }, args) => {
const { name } = args
return await db.insert('templates', {
userId: user._id,
name
})
}
})
I Get:
convex/templates.ts:54:21 - error TS7031: Binding element 'db' implicitly has an 'any' type.

handler: async ({ db, user }, args) => {


convex/templates.ts:54:25 - error TS7031: Binding element 'user' implicitly has an 'any' type.

54 handler: async ({ db, user }, args) => {

convex/templates.ts:54:33 - error TS7006: Parameter 'args' implicitly has an 'any' type.
I Get:
convex/templates.ts:54:21 - error TS7031: Binding element 'db' implicitly has an 'any' type.

handler: async ({ db, user }, args) => {


convex/templates.ts:54:25 - error TS7031: Binding element 'user' implicitly has an 'any' type.

54 handler: async ({ db, user }, args) => {

convex/templates.ts:54:33 - error TS7006: Parameter 'args' implicitly has an 'any' type.
Any ideas?
Discord
Discord - A New Way to Chat with Friends & Communities
Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.
Michal Srb
Michal Srb10mo ago
Where are you importing authMutation from? What is its definition?
oferitz
oferitz10mo ago
@Michal Srb 1. convex/utils.ts 2.
export const authMutation = customMutation(
mutation,
customCtx(async (ctx) => ({ user: await getUserOrThrow(ctx) }))
)

async function getUserOrThrow(ctx: QueryCtx | MutationCtx) {
const userId = await getUserId(ctx)

if (!userId) {
throw new ConvexError('must be logged in')
}

const user = await ctx.db
.query('users')
.withIndex('by_userId', (q) => q.eq('userId', userId))
.first()

if (!user) {
throw new ConvexError('user not found')
}

return user
}
export const authMutation = customMutation(
mutation,
customCtx(async (ctx) => ({ user: await getUserOrThrow(ctx) }))
)

async function getUserOrThrow(ctx: QueryCtx | MutationCtx) {
const userId = await getUserId(ctx)

if (!userId) {
throw new ConvexError('must be logged in')
}

const user = await ctx.db
.query('users')
.withIndex('by_userId', (q) => q.eq('userId', userId))
.first()

if (!user) {
throw new ConvexError('user not found')
}

return user
}
Michal Srb
Michal Srb10mo ago
The error suggests that typescript cannot correctly infer the type for authMutation. I can't see anything wrong with your code. Do you also get the same error in your editor or when running typescript standalone from the command line?
oferitz
oferitz10mo ago
You mean running tsc?
Michal Srb
Michal Srb10mo ago
Yeah
oferitz
oferitz10mo ago
I haven't tried yet. I'll need to set up everything again since I moved to a workaround involving Convex calling a Next.js API route to handle the sending. @Michal Srb Same with running tsc
Michal Srb
Michal Srb10mo ago
Can you share: 1. The full error details 2. The full files including imports 3. Your package.json (one thing worth trying might be to upgrade to latest Convex and convex-helpers version if you're not on them already) What type do you see when you hover authMutation in your editor?
oferitz
oferitz10mo ago
@Michal Srb Thanks, appreciate the effort to help, but I have fully moved to a solution where Convex is sending an API request to my Next.js backend (https://discord.com/channels/1019350475847499849/1019350478817079338/1214518908623654972), which handles the actual email sending. This also eliminates the CSS files limitation mentioned above.
Discord
Discord - A New Way to Chat with Friends & Communities
Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.
tiernacity
tiernacityOP10mo ago
Tying-up the @react-email/tailwind side of this thread - using external packages in my action was sufficient to get that all working

Did you find this page helpful?