Using Tailwind
How ETailwind inlines utilities, how nested-component classes are reached, and the exact documented limitations.
Wrap an email document in <ETailwind> and Tailwind v4 utility classes on descendant elements are compiled into email-safe output at render time. Compatible declarations become inline styles; the boundary is server-only and requires a <head> inside it.
<ETailwind>
<EHtml>
<EHead />
<EBody class="bg-gray-100">
<EContainer class="p-4">
<EText class="text-red-500">Styled with Tailwind</EText>
</EContainer>
</EBody>
</EHtml>
</ETailwind>Precedence and non-inlinable rules
Inlining precedence is component defaults < Tailwind utilities < author style. Utilities that cannot be inlined — media queries and pseudo-classes — are collected into a <style> element in the document <head>, residual class names are sanitized, and mso-* properties survive inlining. If a class needs a <head> and none exists inside the boundary, rendering throws with a message naming the offending classes.
You can extend the engine through props:
config— a Tailwind config (everything exceptcontent), matching React Email'sTailwindConfig.theme— raw CSS appended to the@themelayer.utility— raw CSS appended to the utilities layer.
Share tokens, not the Nuxt stylesheet
Email rendering does not load the Nuxt application's CSS bundle, and browser CSS variables cannot be relied on in email clients. A class such as text-primary must resolve to a concrete value while the email renders.
Keep one application-owned token source and feed its value into ETailwind explicitly:
<script setup lang="ts">
import { emailTheme } from '~/shared/email-theme'
</script>
<template>
<ETailwind :theme="emailTheme">
<EHtml>
<EHead />
<EBody>
<EText class="text-primary">Concrete email color</EText>
</EBody>
</EHtml>
</ETailwind>
</template>export const emailTheme = '@theme { --color-primary: #2563eb; }'The same token module can generate or inform your Nuxt theme, but Nuxt Email does not add a second CSS discovery path or silently import application styles.
ETailwind accepts the config, theme, and utility values you pass directly. It does not resolve arbitrary CSS file imports or CSS @plugin module imports at render time; supply executable Tailwind plugins through config. This keeps production rendering independent of the application filesystem.
Classes inside nested components
Classes written directly in the email template are resolved during rendering and the final marker-scoped post-render pass. Classes emitted inside a nested component — a component whose own render outputs the class-bearing markup — are handled too, so a reusable card or button component styled with Tailwind works exactly like inline markup:
<template>
<div class="bg-red-500 p-4 md:text-lg">
<EText class="m-0">Title</EText>
<EButton class="bg-blue-600 px-4 py-2" href="https://example.com">Open</EButton>
</div>
</template><ETailwind>
<EHtml>
<EHead />
<EBody>
<Card />
</EBody>
</EHtml>
</ETailwind>How this works: E* primitives with style logic (EBody, EText, EButton, ESection, EContainer, ERow, ELink, EImg, EHr) resolve their own Tailwind classes before running margin/padding/Outlook derivation, so utilities behave identically inline or from a nested component. Plain HTML elements and the structural primitives (EHtml, EHeading, EColumn) are inlined after render by a marker-scoped string pass that leaves every other byte — MSO conditional comments included — untouched. Non-inlinable rules from nested classes (such as md:text-lg above) still reach the <head> <style>, and the missing-<head> error still fires when such a class has nowhere to go.
Documented limitations
ECodeInline,EMarkdown,EPreview, andEFontdo not treat a nestedclassas a Tailwind style target — theirclass/head semantics differ.- Nested
<ETailwind>boundaries are not a supported configuration. - Arbitrary CSS imports and CSS
@pluginmodule imports are not resolved during rendering; executable plugins belong inconfig. - Responsive or pseudo-class padding on
EContainer,ESection, orERowremains a CSS rule on the table; use an innerEColumnwhen that padding must survive clients that force collapsed table borders. - Output tracks the pinned Tailwind version compiled by the engine.
Emails that do not use <ETailwind> are entirely unaffected — the nested support adds zero cost to them. The seventeen ETailwind conformance cases (basic inlining, media queries, MSO preservation, nested components, residual-class sanitization, and more) are listed in the conformance report.