Skip to main content

Conformance & 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 conformance report in the repository is the authoritative source; this page summarizes it honestly.

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, pinned commits, and hashes. This page explains the method without copying 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 are collected into a <style> in the <head> (a <head> inside <ETailwind> is required, otherwise rendering throws), residual class names are sanitized, and mso-* properties survive inlining.
  • ETailwind reaches classes inside nested components via provide/inject for style-bearing primitives and a marker-scoped post-render pass for plain and structural elements. 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 explicitly. Its runnable component scope is the eighteen built-ins; opt-in ECodeBlock is recorded as a deliberate divergence from React Email's CodeBlock. It is not a claim that every React Email component or option has a Nuxt Email equivalent. Treat the generated report in the repository, not this page, as the authoritative matrix of cases, classifications, divergences, and unsupported surface.