Skip to content

fix(render): lazy-load prettier so it's only loaded when pretty is used - #3790

Merged
felipefreitag merged 1 commit into
resend:canaryfrom
AmineBenzaazaa:fix/render-prettier-turbopack-bun
Oct 15, 2026
Merged

felipefreitag merged 1 commit into
resend:canaryfrom
AmineBenzaazaa:fix/render-prettier-turbopack-bun

Conversation

@AmineBenzaazaa

@AmineBenzaazaa AmineBenzaazaa commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #3781

Problem

pretty.ts imported prettier/standalone and prettier/plugins/html at module top level, so every render() loaded prettier, even without pretty: true.

Next.js always externalizes prettier. In next dev, Turbopack loads externals through a hashed alias symlink (.next/dev/node_modules/prettier-<hash>) that it creates after the server process has started. Bun's resolver doesn't see a node_modules symlink created after the directory was first resolved (oven-sh/bun#42776, oven-sh/bun#25370), so the import fails with Cannot find package 'prettier-<hash>'. Node resolves it fine, and next build + next start on Bun also works because the symlink already exists at startup.

Fix

Import prettier dynamically the first time pretty() is called (cached afterwards), the same way render already loads react-dom/server. pretty was already async, so the public API is unchanged. As a side effect, render() without pretty no longer pays prettier's load cost.

Verification

Packed this build and installed it into the reproduction (https://github.com/pthneo/react-email-turbopack-bun-prettier-bug, Next 16.4.0, Bun 1.4.2):

  • render() without pretty: works on Turbopack + Bun (previously failed), Turbopack + Node, webpack + Bun, webpack + Node.
  • render(..., { pretty: true }): works on Turbopack + Node, webpack + Bun and webpack + Node. On Turbopack + Bun in dev it still hits the Bun resolver bug; transpilePackages: ["prettier"] in next.config works around it.

Added a test that prettier isn't loaded until pretty is called. @react-email/render tests pass (90/90) and biome check is clean.

馃 Generated with Claude Code


Summary by cubic

Lazy-loads prettier so render no longer loads it unless pretty: true is used.

Previously pretty.ts imported prettier/standalone and prettier/plugins/html at module load time, so every render() call loaded prettier. In Next.js dev with Turbopack, prettier is externalized and resolved through a hashed symlink Bun can't pick up after startup, which made render() fail with "Failed to load external module prettier". Prettier is now imported dynamically on the first pretty() call, keeping the public API unchanged.

  • Fixes render() failing in Next.js dev with Turbopack on Bun.
  • render() without pretty no longer pays prettier's load cost.
  • Adds a test verifying prettier isn't loaded until pretty is called.

Written for commit c437dc7. Summary will update on new commits.

View guided diff

`pretty.ts` imported `prettier/standalone` and `prettier/plugins/html` at
the top level, so every `render()` call loaded prettier, even without
`pretty: true`. Next.js externalizes `prettier`, and in `next dev` Turbopack
loads it through a hashed alias symlink (`.next/dev/node_modules/prettier-<hash>`)
that it creates after the server process has started. Bun's resolver
doesn't pick up that late-created symlink, so loading the external fails.

Prettier is now imported dynamically the first time `pretty` is called, so
`render()` without `pretty` never touches it.

Fixes resend#3781

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

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

A member of the Team first needs to authorize it.

@changeset-bot

changeset-bot Bot commented Oct 14, 2026

Copy link
Copy Markdown

馃 Changeset detected

Latest commit: c437dc7

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

This PR includes changesets to release 1 package
Name Type
@react-email/render 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

@github-actions github-actions Bot added the linear-synced PR has been synced to Linear label Oct 15, 2026
@pkg-pr-new

pkg-pr-new Bot commented Oct 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@react-email/render@3790

commit: c437dc7

@felipefreitag
felipefreitag merged commit f356e81 into resend:canary Oct 15, 2026
9 of 11 checks passed
@AmineBenzaazaa

Copy link
Copy Markdown
Contributor Author

Thanks for the review and merge, @felipefreitag! Happy to help with the remaining pretty: true case if a Bun-side fix lands.

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

Labels

linear-synced PR has been synced to Linear

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error: Failed to load external module prettier (Bun) (Turbopack) (Next.js)

2 participants