Tailwind
Tailwind CSS support for email templates with automatic style inlining.
The Tailwind component wraps your email tree, collects all className values from child elements, compiles them with Tailwind CSS v4's compile() API, and inlines the resulting styles on each element.
Non-inlinable styles (media queries, pseudo-classes) are injected as a <style> tag inside <Head>. If these classes are used but no <Head> component is found, an error is thrown.
Import
tsx
import { Tailwind } from "@unmail/react";vue
<script setup>
import { Tailwind } from "@unmail/vue";
</script>Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode / slot | — | The email component tree to process. |
config | TailwindConfig | — | Tailwind configuration (optional). |
ts
type TailwindConfig = Omit<Config, "content">;The content field is omitted because classes are auto-detected from the component tree.
Usage
Basic
tsx
import { Tailwind, Html, Head, Body, Text } from "@unmail/react";
export function Email() {
return (
<Tailwind>
<Html>
<Head />
<Body className="bg-white">
<Text className="text-lg font-bold text-gray-900">
Hello world
</Text>
</Body>
</Html>
</Tailwind>
);
}vue
<script setup>
import { Tailwind, Html, Head, Body, Text } from "@unmail/vue";
</script>
<template>
<Tailwind>
<Html>
<Head />
<Body class="bg-white">
<Text class="text-lg font-bold text-gray-900">
Hello world
</Text>
</Body>
</Html>
</Tailwind>
</template>With pixelBasedPreset
Email clients don't support rem units. The pixelBasedPreset overrides Tailwind's default rem-based spacing and font sizes with pixel equivalents.
tsx
import { Tailwind } from "@unmail/react";
import { pixelBasedPreset } from "@unmail/react/tailwind";
export function Email() {
return (
<Tailwind config={{ presets: [pixelBasedPreset] }}>
<Html>
<Head />
<Body className="bg-white">
<Text className="text-base p-4">Hello world</Text>
</Body>
</Html>
</Tailwind>
);
}vue
<script setup>
import { Tailwind, Html, Head, Body, Text } from "@unmail/vue";
import { pixelBasedPreset } from "@unmail/vue/tailwind";
</script>
<template>
<Tailwind :config="{ presets: [pixelBasedPreset] }">
<Html>
<Head />
<Body class="bg-white">
<Text class="text-base p-4">Hello world</Text>
</Body>
</Html>
</Tailwind>
</template>Sample values from the preset:
| Category | Class | Value |
|---|---|---|
| Font size | text-xs | 12px |
| Font size | text-sm | 14px |
| Font size | text-base | 16px |
| Font size | text-lg | 18px |
| Font size | text-xl | 20px |
| Font size | text-2xl | 24px |
| Spacing | p-1, m-1 | 4px |
| Spacing | p-2, m-2 | 8px |
| Spacing | p-3, m-3 | 12px |
| Spacing | p-4, m-4 | 16px |
Custom config
tsx
import { Tailwind, Html, Head, Body, Text } from "@unmail/react";
import { pixelBasedPreset } from "@unmail/react/tailwind";
export function Email() {
return (
<Tailwind
config={{
presets: [pixelBasedPreset],
theme: {
extend: {
colors: {
brand: "#007bff",
},
},
},
}}
>
<Html>
<Head />
<Body>
<Text className="text-brand font-bold">Branded text</Text>
</Body>
</Html>
</Tailwind>
);
}vue
<script setup>
import { Tailwind, Html, Head, Body, Text } from "@unmail/vue";
import { pixelBasedPreset } from "@unmail/vue/tailwind";
const tailwindConfig = {
presets: [pixelBasedPreset],
theme: {
extend: {
colors: {
brand: "#007bff",
},
},
},
};
</script>
<template>
<Tailwind :config="tailwindConfig">
<Html>
<Head />
<Body>
<Text class="text-brand font-bold">Branded text</Text>
</Body>
</Html>
</Tailwind>
</template>Notes
<Tailwind>must wrap the entire email tree (outside<Html>).- A
<Head>component must exist inside<Tailwind>for media query and pseudo-class support. - Tailwind CSS v4 is used under the hood.
- Uses React Suspense internally, which is handled automatically by the
render()function.