Skip to main content

Conformance and divergences

How compatibility is measured against React Email, the current pass numbers, and every intentional divergence.

Compatibility is reported per behavior. No global "React Email compatible" percentage is claimed. The generated repository report is the authoritative source; this page explains its method.

How it is measured

The current Nuxt Email candidate is compared against React Email 6.9.0 and @react-email/render 2.1.0. Each behavior is a case that renders the same input through both renderers and asserts one of these classifications:

  • exact — byte-identical output.
  • normalized — identical after the internal conformance helper canonicalizes insignificant serialization differences such as attribute order, trailing style semicolons, class whitespace, and self-closing form.
  • semantic — specific asserted properties match.
  • intentional-divergence — output differs on purpose, for a documented email-safety or Vue-authoring reason.
  • unsupported — a React Email component that is not ported.

Current results

The generated repository report is the only source for current counts, classifications, fixed commits, and hashes. This page does not copy values that can drift. See the generated conformance report and its provenance record.

Intentional divergences

CaseReason
html-defaultsVue SSR does not inject React 19's implicit empty head into a standalone html element.
preview-max-lengthEPreview omits React 19 title output and keeps its hiding and plain-text exclusion invariant, because Vue SSR cannot safely hoist title into head.
preview-shortReact 19 hoists Preview title into head; Vue authors place title explicitly in EHead. EPreview keeps hiding styles and data-skip-in-text fixed so user attributes cannot expose filler.
preview-style-overrideReact replaces all hiding styles when a user style is provided; EPreview retains the hiding defaults as an email-safety invariant.
preview-unicode-boundaryReact truncates at 200 UTF-16 code units and can split a surrogate pair; EPreview drops the whole boundary code point and fills the remaining position.
row-columnsEColumn omits React Email's internal data-id marker because no Vue behavior consumes it.

Additional documented differences

  • EMarkdown drops data-id. React Email wraps Markdown in <div data-id="react-email-markdown">; EMarkdown omits the marker (the same no-data-id divergence as EColumn).
  • Presentation tables reject fixed attributes. ESection, EContainer, and ERow throw a TypeError when passed border, cellpadding, cellspacing, or role. React Email silently discards these overrides; Nuxt Email fails loudly to keep the email-safe table layout an invariant.
  • ECodeInline excludes its compatibility copy from plain text. HTML retains the hidden Orange.fr fallback span, but plain-text conversion skips it so recipients receive the code once. React Email emits it twice.
  • ETailwind moves non-inlinable rules to <head>. Media-query and pseudo-class rules move to a <style> in <head>. This needs a head inside ETailwind; rendering fails without one. Residual class names are sanitized. Outlook-specific (mso-*) properties survive inlining.
  • ETailwind reaches classes inside nested components. Style-bearing primitives resolve their classes during rendering. A second pass handles marked plain and structural elements without changing other output. Nested ETailwind boundaries are not supported. See the Tailwind guide.

Scope, not a parity guarantee

The report records zero failing runnable cases and lists unsupported React components. Its runnable scope is the eighteen built-ins. It records opt-in ECodeBlock as an intentional difference from React Email's CodeBlock.

This does not mean every React Email component or option has a Nuxt Email equivalent. Use the generated repository report as the authoritative matrix of cases, classifications, differences, and unsupported surface.