Skip to content

Resend integration always creates duplicate templates instead of updating existing ones #3194

@cooperjbrandon

Description

@cooperjbrandon

Summary

The "Upload to Resend" / "Bulk Upload" buttons in the preview server's Resend toolbar always call resend.templates.create(), so every click creates a brand new template in the Resend dashboard rather than updating the existing one. This makes the integration effectively unusable as a sync mechanism — you end up with welcome, welcome (1), welcome (2), etc., and the template ID referenced by your application code keeps pointing at the original (outdated) version.

Reproduction

  1. Create an email template (e.g. emails/welcome.tsx)
  2. Run pnpm email
  3. Open the template, go to the Resend tab in the toolbar, click Upload. A new template is created in Resend — copy its ID into your app code.
  4. Edit emails/welcome.tsx (e.g. change a heading)
  5. Click Upload again
  6. Expected: the same template (by ID) is updated with the new HTML
  7. Actual: a second template named welcome is created with a new ID; the original template still has the old HTML, and the app code still points at it

Source

In packages/preview-server/src/actions/export-single-template.ts:

const response = await resend.templates.create({
  name: parsedInput.name,
  html: parsedInput.html,
});

It only calls create, never update. The UI also has no way to associate a template file with an existing Resend template ID — neither through the toolbar nor a config file.

Suggested fix

Two parts:

  1. Per-template ID mapping: support an optional config (e.g. react-email.config.ts or .react-email.json) that maps email file paths/slugs to existing Resend template IDs:
 export default {
   resend: {
     templates: {
       "welcome": "tmpl_abc123",
       "password-reset": "tmpl_def456",
     },
   },
 };
  1. Use update when an ID is known: in exportSingleTemplate, if the slug is in the mapping, call resend.templates.update(id, { html }); otherwise fall through to create() and (ideally) write the new ID back to the config so the next upload updates in place.

A simpler MVP would just look up the template by name via resend.templates.list() and call update() if a match exists — no config file needed — but a stable ID mapping is more reliable since template names aren't unique in Resend.

Workaround

I wrote a small standalone script that uses @react-email/render + resend.templates.update(id, { html }) directly with hardcoded IDs. Happy to share if useful, and happy to put up a PR for the upstream fix if there's interest in either of the approaches above.

Environment

  • react-email: 5.2.10
  • @react-email/preview-server: 5.2.10
  • resend: 6.x

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions