Skip to content

fix(render): remove nul bytes when using React 18 #3406

Open
rockingskier wants to merge 1 commit intoresend:canaryfrom
rockingskier:patch-1
Open

fix(render): remove nul bytes when using React 18 #3406
rockingskier wants to merge 1 commit intoresend:canaryfrom
rockingskier:patch-1

Conversation

@rockingskier
Copy link
Copy Markdown

@rockingskier rockingskier commented Apr 20, 2026

Workaround for React 18 renderToPipeableStream nul-byte bug

The bug

React 18's server renderer encodes HTML into a reusable 2048-byte Uint8Array using TextEncoder.encodeInto(). When a multi-byte UTF-8 character (e.g. an em-dash —, 3 bytes) would straddle the end of the buffer, encodeInto stops early rather than splitting the character in half, leaving a few trailing bytes of the buffer unwritten.

The bug is that React flushes the entire 2048-byte buffer instead of just the portion that was actually written, so those uninitialised trailing bytes (\0) leak into the output stream.

The result is HTML that contains stray nul bytes near internal 2KB boundaries. Most browsers shrug these off, but many email clients interpret them as end-of-file and silently truncate the email at the first nul byte.

Trigger conditions are narrow but absolutely reachable in real emails:

  • A boundary (forces Fizz to take the buffered path).
  • Enough content to fill roughly 2KB (typical for styled email templates).
  • A multi-byte UTF-8 character (em-dash, en-dash, emoji 💀, curly quotes, etc.) landing at the buffer boundary.

The fix in React

Facebook fixed this upstream in facebook/react#26228 by slicing the buffer to the number of bytes actually written before flushing.

The fix shipped in React 19 and was deliberately not backported to the 18.x line.

The fix in React Email

Since @react-email/render still supports React 18, we need to work around the bug by stripping nul bytes from the HTML after renderToPipeableStream finishes:

html = await readStream(stream).then(stripNulBytes);

This is scoped to the renderToPipeableStream code path (React 18 in Node). The renderToReadableStream path untouched since it's unaffected by the bug. Nul bytes have no legitimate place in HTML output, so stripping them unconditionally is safe.

References

Upstream fix: facebook/react#26228
Affected: react-dom@18.0.0 - 18.3.1
Fixed in: react-dom@19.0.0+

Fixes

Among others I'm sure


Summary by cubic

Remove stray NUL bytes from HTML when using React 18 server rendering (renderToPipeableStream) to prevent emails from being cut off in clients like Outlook. We now strip \0 characters after the stream completes as a safe workaround for a React 18 bug fixed in React 19.

Written for commit 326345e. Summary will update on new commits.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 20, 2026

⚠️ No Changeset found

Latest commit: 326345e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Apr 20, 2026

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

A member of the Team first needs to authorize it.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 20, 2026

Open in StackBlitz

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

commit: 326345e

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.

No issues found across 1 file

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

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.

1 participant