Skip to content

fix(ui): update existing Resend templates instead of creating duplicates#3278

Open
tazwar9t62 wants to merge 8 commits intoresend:canaryfrom
tazwar9t62:fix/3194-resend-template-upsert
Open

fix(ui): update existing Resend templates instead of creating duplicates#3278
tazwar9t62 wants to merge 8 commits intoresend:canaryfrom
tazwar9t62:fix/3194-resend-template-upsert

Conversation

@tazwar9t62
Copy link
Copy Markdown

@tazwar9t62 tazwar9t62 commented Apr 15, 2026

Summary

Fixes #3194.

The Resend integration in the preview server always created a new template on every upload. That made repeated uploads produce duplicate templates instead of updating the original one.

This PR changes the upload flow to behave like an upsert:

  • use a deterministic Resend alias per template
  • update an existing template when a match is found
  • only create a new template when no existing template can be matched

Changes

  • added a dedicated Resend template upsert helper in packages/preview-server/src/utils/resend-template-upsert.ts
  • added focused tests for alias generation and matching behavior in packages/preview-server/src/utils/resend-template-upsert.spec.ts
  • updated packages/preview-server/src/actions/export-single-template.ts to use the upsert flow instead of calling resend.templates.create() directly
  • updated packages/preview-server/src/components/toolbar/resend.tsx so both single upload and bulk upload use the same normalized template identity
  • updated the toolbar messaging so the UI distinguishes between template creation and template update

Why

Previously the preview server always called resend.templates.create(), so every upload created a new dashboard template.

This implementation introduces a stable alias for each template and uses it to find and update the same template on future uploads. It also includes a fallback for older templates created before aliases were used, including older single-upload names that included the file extension.

That keeps new uploads stable while still allowing existing templates to be migrated into the new flow.

Verification

Ran:

  • pnpm --filter @react-email/preview-server exec vitest run src/utils/resend-template-upsert.spec.ts
  • pnpm exec biome check packages/preview-server/src/utils/resend-template-upsert.ts packages/preview-server/src/utils/resend-template-upsert.spec.ts packages/preview-server/src/actions/export-single-template.ts packages/preview-server/src/components/toolbar/resend.tsx

Results:

  • focused tests passed
  • formatting and lint checks passed on touched files

Note:

  • pnpm --filter @react-email/preview-server exec tsc --noEmit still reports existing package-wide type resolution issues unrelated to this patch

Summary by cubic

Prevents duplicate Resend templates by upserting on upload in @react-email/ui. Uses a stable alias and a paginated lookup to update existing templates, and updates the toolbar to show when a template was created vs updated.

  • Bug Fixes
    • Added upsertResendTemplate to generate a deterministic alias and update existing templates; only creates when no match is found (with fallback to legacy names, including those with extensions).
    • Implemented paginated template lookup and selected the oldest exact name match when the alias isn’t present; tightened result narrowing for CI.
    • Switched single and bulk uploads to use normalized template names via removeFilenameExtension and the upsert flow; pass legacyName for migration.
    • Updated UI messaging to distinguish “created” vs “updated” and return/display the operation in results; added focused tests for alias generation, matching, and pagination.

Written for commit 355eaa4. Summary will update on new commits. Review in cubic

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 15, 2026

@tazwar9t62 is attempting to deploy a commit to the resend Team on Vercel.

A member of the Team first needs to authorize it.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 15, 2026

🦋 Changeset detected

Latest commit: 355eaa4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@react-email/ui Patch
react-email Patch
@react-email/editor Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 15, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@react-email/ui@3278

commit: 355eaa4

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files

Confidence score: 3/5

  • There is a concrete production risk in packages/preview-server/src/utils/resend-template-upsert.ts: new SDK calls (templates.get, templates.list, templates.update) may fail if current Resend API keys lack the required permissions, which could break template upsert behavior at runtime.
  • The fallback lookup in packages/preview-server/src/utils/resend-template-upsert.ts only inspects the first 100 templates; without pagination, larger accounts may miss existing templates and create duplicates.
  • Given one high-severity, high-confidence operational risk plus a medium data-consistency risk, this looks mergeable with caution rather than a low-risk merge.
  • Pay close attention to packages/preview-server/src/utils/resend-template-upsert.ts - verify API key scopes for new template operations and add pagination to prevent duplicate template creation.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/preview-server/src/utils/resend-template-upsert.ts">

<violation number="1" location="packages/preview-server/src/utils/resend-template-upsert.ts:162">
P1: Custom agent: **API Key Permission Check SDK Methods**

This change introduces new Resend SDK operations (`templates.get`, `templates.list`, `templates.update`). Confirm production API keys include permissions for template read/update operations, not just create, to prevent post-deploy permission failures.</violation>

<violation number="2" location="packages/preview-server/src/utils/resend-template-upsert.ts:188">
P2: Fallback lookup only checks the first 100 templates with no pagination, so existing templates outside that page won’t be found and a duplicate can be created for large accounts.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread packages/ui/src/utils/resend-template-upsert.ts
Comment thread packages/preview-server/src/utils/resend-template-upsert.ts Outdated
Comment thread .changeset/four-zebras-hear.md Outdated
@tazwar9t62 tazwar9t62 requested a review from gabrielmfern April 15, 2026 10:33
@gabrielmfern gabrielmfern force-pushed the fix/3194-resend-template-upsert branch from aebd54e to 1f69d62 Compare April 27, 2026 15:21
@gabrielmfern gabrielmfern changed the title fix(preview-server): update existing Resend templates instead of creating duplicates fix(ui): update existing Resend templates instead of creating duplicates Apr 27, 2026
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 1 file (changes from recent commits).

Shadow auto-approve: would require human review. Introduces non-trivial logic for template upserting, deterministic hashing, and paginated API lookups, which requires human verification of the migration and matching strategy.

Copy link
Copy Markdown
Member

@gabrielmfern gabrielmfern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems overly complicated at the upsert part

): Promise<ResendResponse<ResendTemplateIdentifier>>;
}

interface ResendApi {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this and aren't just using the native node SDK?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Resend integration always creates duplicate templates instead of updating existing ones

2 participants