`.** It covers the full padded area including the padding space.
- **`text` takes precedence over `children`** when both are supplied.
- **Link styles are only injected when `text` is a string.** React node `children` receive no automatic link styling — style links manually in that case.
- **`listStyle` is informational.** It is stored in config and consumed by the builder UI, but is not applied as CSS by this component's renderer.
- **`opacity` applies to the inner `
`**, not the `
`. This means `backgroundColor` (on the ` | `) is not affected by `opacity` — the background remains fully opaque even when `opacity < 1`.
- **`textAlign` is applied both as CSS and as the HTML `align` attribute** on the ` | ` for Outlook Classic compatibility. No special handling needed — set `textAlign` normally.
--- SECTION: SPACER ---
# Spacer Component — LLM Reference
`Spacer` is a **vertical whitespace primitive**. It renders a fixed-height transparent block that creates consistent vertical spacing between components in an email. It has no visual output beyond the space it occupies — no background, no border, no text. It is the correct tool for adding vertical rhythm between layout elements.
---
## Position in the Document Tree
```
DocumentTree
└── Section
└── Container
└── Column
└── Heading (Text)
└── Spacer ← vertical gap between elements
└── Text
└── Spacer ← vertical gap before button
└── Button
```
`Spacer` is a leaf node. It can be placed anywhere a child component is accepted: inside `Column`, `Row`, directly in a `Container` cell, or between any sibling components.
---
## ComponentNode JSON Structure
```json
{
"id": "unique-string-id",
"type": "SpacerComponent",
"config": {
"height": "24px",
"hideOnMobile": false
}
}
```
---
## `SpacerConfig` — Full Property Reference
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
| `height` | `string` | ✓ Yes | — | Height of the vertical space. Must be a px value (e.g. `"16px"`, `"48px"`). |
| `hideOnMobile` | `boolean` | No | `false` | When `true`, adds the `hide-on-mobile` CSS class to the outer table. The builder's global stylesheet collapses the spacer on mobile viewports. |
---
## How It Works
`Spacer` creates vertical space using three reinforcing techniques for cross-client reliability:
1. **CSS `height`** on the ` | ` — respected by modern email clients
2. **HTML `height` attribute** on both the `` and the `| ` — required for Outlook Classic (Word rendering engine), which ignores CSS height on table cells
3. **`fontSize: "0"` and `lineHeight: "0"`** on the ` | ` — suppresses any phantom vertical space that font metrics would otherwise add, even with no visible text content
4. **` `** inside the ` | ` — ensures the cell renders in clients that collapse empty cells
The outer table uses `width: 100%` so the spacer spans the full width of its parent column, maintaining layout integrity.
---
## `height` Format
`height` must be a **pixel string** (e.g. `"24px"`). The numeric value is parsed and applied as both the CSS `height` and the HTML `height` attribute. Non-px values (e.g. `"1.5rem"`, `"10%"`) will have their integer portion extracted via `parseInt` — the `rem`/`%` unit will be silently dropped and the raw integer used as pixels.
Always use `px` units for predictable behavior.
```json
// Correct
"height": "32px"
// Avoid — unit stripped, only "10" used
"height": "10%"
```
---
## `hideOnMobile`
When `hideOnMobile: true`, the outer table receives the `hide-on-mobile` CSS class. The builder's global `` stylesheet sets `display: none` on this class for mobile viewports. This allows a large desktop spacer to be hidden on mobile without a second Spacer component.
```json
// Large desktop spacer, hidden on mobile
{
"id": "desktop-spacer",
"type": "SpacerComponent",
"config": {
"height": "48px",
"hideOnMobile": true
}
}
// Paired small mobile spacer (always visible)
{
"id": "mobile-spacer",
"type": "SpacerComponent",
"config": {
"height": "16px"
}
}
```
---
## React Usage
```tsx
import Spacer, { SpacerConfig } from './Spacer';
const config: SpacerConfig = {
height: '24px',
};
```
The component is memoized via `arePropsEqual`. Pass a stable `config` reference to avoid re-renders.
---
## Spacer vs `gap` on Parent Components
Both `Spacer` and parent `gap` properties create vertical space, but they serve different purposes:
| | `Spacer` | `gap` on `Column` |
|---|---|---|
| **Mechanism** | Explicit child node in the tree | Config property on the parent |
| **Granularity** | Per-gap, any size | Uniform between all children |
| **Mobile control** | `hideOnMobile` per spacer | Single value for all gaps |
| **Outlook support** | ✓ Full (HTML attribute + CSS) | ✓ Full (spacer ` | ` row) |
| **When to use** | Different spacing between specific pairs of children, or mobile-responsive spacing | Uniform spacing between all children in a column |
Use `gap` on `Column` when all children should be evenly spaced. Use `Spacer` when you need variable spacing between specific elements, or when you need mobile-specific control over individual gaps.
---
## Common Patterns & Examples
### 1. Standard content gap
```json
{
"id": "spacer-16",
"type": "SpacerComponent",
"config": { "height": "16px" }
}
```
---
### 2. Section breathing room (top/bottom of a column)
```json
{
"id": "spacer-top",
"type": "SpacerComponent",
"config": { "height": "40px" }
}
```
---
### 3. Large desktop spacer, hidden on mobile
```json
{
"id": "spacer-hero-gap",
"type": "SpacerComponent",
"config": {
"height": "64px",
"hideOnMobile": true
}
}
```
---
### 4. Spacer between heading and body text
```json
[
{
"id": "heading",
"type": "TextComponent",
"config": {
"text": "Our latest products",
"fontSize": "24px",
"fontWeight": "700"
}
},
{
"id": "spacer-heading-body",
"type": "SpacerComponent",
"config": { "height": "12px" }
},
{
"id": "body",
"type": "TextComponent",
"config": {
"text": "Discover our new arrivals this season. ",
"fontSize": "16px"
}
}
]
```
---
## Data Bindings
`Spacer` supports the `bindings` property on the `ComponentNode`, alongside `id`, `type`, and `config`. Leaf components do not support `dataList` or `itemAlias` — use those on a parent `Container`, `Column`, or `Row` to repeat the component. `visible` and `propertyMap` are fully supported.
```json
{
"id": "dynamic-spacer",
"type": "SpacerComponent",
"bindings": {
"visible": "layout.showSpacing === true"
},
"config": {
"height": "32px"
}
}
```
| Property | Description |
|---|---|
| `visible` | JS expression evaluated against the Data Layer. Hides this component when falsy. |
| `propertyMap` | Maps component property paths (prefixed with `config.`) to Data Layer paths. |
> `Spacer` has no meaningful `propertyMap` targets beyond `config.height`. Conditional rendering via `visible` is the primary binding use case.
---
## Rules & Constraints
- **`height` is required.** There is no default — omitting it will cause a broken render. Always provide a `px` string value.
- **Use `px` units only.** Non-px values are silently truncated to their integer portion and treated as pixels. `"1.5rem"` becomes `1`, `"10%"` becomes `10` — both likely unintended.
- **`Spacer` has no visual properties.** It cannot have a background color, border, or content. It is transparent by design. To create a visible divider, use a `Container` or `Row` with a `border` instead.
- **Do not use `Spacer` for horizontal spacing.** It always renders at `width: 100%`. Horizontal gaps between siblings are handled by the `gap` property on `Container`, `Column`, or `Row`.
- **`hideOnMobile` requires the global stylesheet.** The `hide-on-mobile` class has no effect without the builder's `` CSS. In isolated rendering contexts, the spacer will always be visible regardless of this flag.
- **Minimum effective height is `1px`.** `parseInt` falls back to `1` if the height string produces `0` or `NaN`. A `"0px"` spacer renders with `height: 1` in Outlook — use the `Column` or `Row` `gap` property instead if you need truly zero space.
--- SECTION: SECTION ---
# Section Component — LLM Reference
`Section` is the **top-level structural wrapper** for an email. It renders as a full-width `` that spans the entire email width and acts as the outermost shell for a band of content. A template contains **exactly three Sections** — `header`, `content`, and `footer` — in fixed positions. They cannot be added, removed, reordered, or nested.
Sections are **not** layout containers — they don't distribute children side-by-side. That is the job of `Container`. A Section holds one or more `Container` components stacked vertically as children, and those Containers handle column logic.
---
## Position in the Document Tree
```
Body (ComponentNode)
└── children (Array)
└── Section ← Full-width band
└── Container ← Layout & width control
└── Column ← Vertical stacking
```
A `Section` is always a **root-level node** in the `DocumentTree`. There are exactly three — `header`, `content`, `footer` — and their positions are fixed. They are never nested inside each other or inside a `Container`.
---
## ComponentNode JSON Structure
```json
{
"id": "unique-string-id",
"type": "SectionComponent",
"config": {
"sectionType": "content",
"backgroundColor": "#f3f4f6",
"padding": "24px 0",
"gap": "16px",
"border": {
"bottom": { "width": "1px", "style": "solid", "color": "#e5e7eb" }
},
"backgroundImage": {
"src": "https://example.com/bg.jpg",
"repeat": "no-repeat",
"size": "cover",
"position": "center center"
}
},
"children": []
}
```
---
## `SectionConfig` — Full Property Reference
### Required
| Property | Type | Options | Description |
|---|---|---|---|
| `sectionType` | `string` | `"header"`, `"footer"`, `"content"` | Semantic role of the section. Used for labeling and dev tooling — does not affect visual rendering. |
### Layout
| Property | Type | Example | Description |
|---|---|---|---|
| `padding` | `string` | `"48px 0"` | Inner padding applied to the single `` that wraps all children. CSS shorthand. |
| `gap` | `string` | `"16px"` | Vertical space between children (informational — consumed by the builder's layout engine, not directly rendered by this component). |
### Visual
| Property | Type | Example | Description |
|---|---|---|---|
| `backgroundColor` | `string` | `"#1e1b4b"` | Background fill color for the full-width band |
| `border` | `BorderConfig` | See below | Border applied to the outer `` element |
| `backgroundImage` | `object` | See below | Background image for the section band |
### `BorderConfig`
Specify either a **full border** (all sides) or **individual sides**. Do not mix both in the same object.
```json
// Full border — all sides
"border": {
"width": "1px",
"style": "solid",
"color": "#e5e7eb"
}
// Per-side border
"border": {
"top": { "width": "3px", "style": "solid", "color": "#6366f1" }
}
```
Supported side keys: `"top"`, `"right"`, `"bottom"`, `"left"`.
Each side object requires all three of: `width`, `style`, `color`.
When individual sides are specified, all other sides are explicitly set to `"none"` to prevent Outlook from rendering phantom black borders.
### `backgroundImage`
```json
"backgroundImage": {
"src": "https://example.com/texture.jpg",
"repeat": "no-repeat",
"size": "cover",
"position": "center center"
}
```
| Property | Type | Description |
|---|---|---|
| `src` | `string` | Absolute URL of the image |
| `repeat` | `string` | Any CSS `background-repeat` value: `"no-repeat"`, `"repeat"`, `"repeat-x"`, `"repeat-y"` |
| `size` | `string` | Any CSS `background-size` value: `"cover"`, `"contain"`, `"auto"` |
| `position` | `string` | Any CSS `background-position` value: `"center center"`, `"top left"`, etc. |
---
## Section vs Container — Key Differences
| | `Section` | `Container` |
|---|---|---|
| **Purpose** | Full-width email band | Multi-column layout |
| **Width** | Always 100% | `"full"` or `"fixed"` (e.g. `"600px"`) |
| **Column layout** | ✗ No | ✓ Yes (`childrenConstraints`) |
| **Position in tree** | Root level only | Inside a Section |
| **Stacking direction** | Vertical (Sections stack top-to-bottom) | Horizontal (children placed left-to-right) |
| **Background image** | ✓ Yes | ✓ Yes |
| **Border** | ✓ Yes | ✓ Yes |
| **Border radius** | ✗ No | ✓ Yes |
| **`alignItems`** | ✗ No | ✓ Yes |
| **`justifyContent`** | ✗ No | ✓ Yes |
---
## React Usage
```tsx
import Section, { SectionConfig } from './Section';
const config: SectionConfig = {
sectionType: 'content',
backgroundColor: '#f9fafb',
padding: '32px 0',
};
```
The component is memoized via `arePropsEqual`. Pass stable `config` references (e.g. with `useMemo`) to avoid unnecessary re-renders.
---
## `sectionType` Semantics
`sectionType` is a semantic label — it has no effect on rendered styles but is used by the builder's dev overlay and accessibility labels.
| Value | Intended use |
|---|---|
| `"header"` | Logo bar, navigation, pre-header text |
| `"content"` | Body rows — hero, product blocks, articles, CTAs |
| `"footer"` | Unsubscribe links, legal text, social icons, address |
Each value maps to exactly one Section in the template. There is one `"header"`, one `"content"`, and one `"footer"` — never more.
---
## Common Patterns & Examples
### 1. Minimal content section
```json
{
"id": "section-body",
"type": "SectionComponent",
"config": {
"sectionType": "content",
"backgroundColor": "#ffffff",
"padding": "0"
},
"children": []
}
```
---
### 2. Header section with brand background
```json
{
"id": "section-header",
"type": "SectionComponent",
"config": {
"sectionType": "header",
"backgroundColor": "#1e1b4b",
"padding": "16px 0"
},
"children": [
{
"id": "header-container",
"type": "ContainerComponent",
"config": {
"widthType": "fixed",
"width": "600px",
"childrenConstraints": { "widthDistributionType": "equals" },
"justifyContent": "center",
"padding": "0 24px"
},
"children": []
}
]
}
```
---
### 3. Footer section with top border divider
```json
{
"id": "section-footer",
"type": "SectionComponent",
"config": {
"sectionType": "footer",
"backgroundColor": "#f9fafb",
"padding": "24px 0",
"border": {
"top": { "width": "1px", "style": "solid", "color": "#e5e7eb" }
}
},
"children": []
}
```
---
### 4. Hero section with full-bleed background image
```json
{
"id": "section-hero",
"type": "SectionComponent",
"config": {
"sectionType": "content",
"backgroundColor": "#000000",
"padding": "0",
"backgroundImage": {
"src": "https://example.com/hero.jpg",
"repeat": "no-repeat",
"size": "cover",
"position": "center center"
}
},
"children": [
{
"id": "hero-container",
"type": "ContainerComponent",
"config": {
"widthType": "fixed",
"width": "600px",
"childrenConstraints": { "widthDistributionType": "equals" },
"justifyContent": "center",
"alignItems": "center",
"padding": "80px 40px"
},
"children": []
}
]
}
```
---
### 5. Striped layout using multiple Containers
Since a template has exactly one `content` Section, alternating background rows are achieved with multiple `Container` children stacked inside it — not multiple Sections.
```json
{
"id": "section-content",
"type": "SectionComponent",
"config": { "sectionType": "content", "backgroundColor": "#ffffff", "padding": "0" },
"children": [
{
"id": "row-1",
"type": "ContainerComponent",
"config": { "widthType": "full", "childrenConstraints": { "widthDistributionType": "equals" }, "backgroundColor": "#ffffff", "padding": "32px 24px" },
"children": []
},
{
"id": "row-2",
"type": "ContainerComponent",
"config": { "widthType": "full", "childrenConstraints": { "widthDistributionType": "equals" }, "backgroundColor": "#f3f4f6", "padding": "32px 24px" },
"children": []
},
{
"id": "row-3",
"type": "ContainerComponent",
"config": { "widthType": "full", "childrenConstraints": { "widthDistributionType": "equals" }, "backgroundColor": "#ffffff", "padding": "32px 24px" },
"children": []
}
]
}
```
---
### 6. Section with data binding (conditional visibility)
```json
{
"id": "section-promo",
"type": "SectionComponent",
"bindings": {
"visible": "user.hasActivePromo === true"
},
"config": {
"sectionType": "content",
"backgroundColor": "#fef9c3",
"padding": "16px 0",
"border": {
"bottom": { "width": "2px", "style": "solid", "color": "#facc15" }
}
},
"children": []
}
```
---
## Data Bindings
`Section` supports the `bindings` property on the `ComponentNode`, alongside `id`, `type`, and `config`. Bindings connect this component to the Data Layer for dynamic rendering.
```json
{
"id": "section-promo",
"type": "SectionComponent",
"bindings": {
"visible": "user.hasActivePromo === true",
"propertyMap": {
"config.backgroundColor": "promo.bgColor"
}
},
"config": {
"sectionType": "content",
"backgroundColor": "#fef9c3",
"padding": "16px 0"
},
"children": []
}
```
| Property | Description |
|---|---|
| `dataList` | Not applicable. Sections are fixed structural slots — they cannot be repeated. Use `dataList` on a `Container` child instead. |
| `itemAlias` | Not applicable for the same reason — use on `Container`. |
| `visible` | JS expression evaluated against the Data Layer. Hides the entire section band when falsy. |
| `propertyMap` | Maps `config` property paths to Data Layer paths (e.g. `"config.backgroundColor"` → `"promo.bgColor"`). |
---
## Rules & Constraints
- **Exactly three, fixed positions.** A template always has exactly one `header`, one `content`, and one `footer` Section. They cannot be added, removed, reordered, or nested inside any other component.
- **One `` only.** The Section renders a single cell. All width constraint and column logic must live inside a `Container` child.
- **`padding` applies to the inner cell**, not the outer table. For full-bleed background colors or images, set `padding: "0"` on the Section and add padding inside the child Container instead.
- **`gap` is informational.** The `gap` property on Section is stored in config but is consumed by the builder's drag-and-drop engine for spacing between Sections — it is not applied as CSS by this component.
- **`border` renders on the outer ``.** Unlike `Container`, there is no border-radius support on Section — borders will always be square-cornered.
- **Background image layering.** `backgroundColor` acts as a fallback when the background image fails to load. Always set a contrasting `backgroundColor` alongside any `backgroundImage`.
- **`sectionType` does not affect styles.** It is only a semantic label for dev tooling and accessibility. Any `sectionType` value can use any visual config.
--- SECTION: ROW ---
# Row Component — LLM Reference
`Row` is a **horizontal arrangement primitive** that sits inside a `Container` column. It places its children side-by-side in a single ``, handles gap columns between them, and supports alignment, padding, background styling, borders, links, and mobile stacking. It does **not** control email-level width — that is the job of `Container`.
---
## Position in the Document Tree
```
DocumentTree
└── Section ← full-width band
└── Container ← column layout & email width
└── Row ← horizontal child arrangement
└── Image / Text / Button / ...
```
A `Row` lives inside a `Container` (or another component that acts as a column). Unlike `Container`, a `Row` does not manage fixed vs. full width at the email level — it fills whatever space its parent column provides.
---
## ComponentNode JSON Structure
```json
{
"id": "unique-string-id",
"type": "RowComponent",
"config": {
"gap": "16px",
"justifyContent": "center",
"alignItems": "center",
"fillWidth": false,
"layoutColumns": "equal",
"padding": "12px",
"backgroundColor": "#ffffff",
"borderRadius": "8px",
"border": {
"width": "1px",
"style": "solid",
"color": "#e5e7eb"
},
"backgroundImage": {
"src": "https://example.com/bg.jpg",
"repeat": "no-repeat",
"size": "cover",
"position": "center center"
},
"width": "100%",
"height": "64px",
"innerLink": {
"type": "url",
"url": "https://example.com",
"target": "_blank"
},
"mobile": {
"wrap": true,
"justifyContent": "center",
"alignItems": "start"
}
},
"children": []
}
```
---
## `RowConfig` — Full Property Reference
### Layout
| Property | Type | Example | Description |
|---|---|---|---|
| `gap` | `string` | `"16px"` | Horizontal space between children. Rendered as a gap `| `. Also used as vertical gap between stacked children on mobile. |
| `justifyContent` | `"start" \| "center" \| "end"` | `"center"` | Horizontal alignment of children within the row |
| `alignItems` | `"start" \| "center" \| "end"` | `"center"` | Vertical alignment of children within the row |
| `width` | `string` | `"100%"` | Width of the outer row table. Defaults to `100%`. |
| `height` | `string` | `"80px"` | Fixed height applied to all table levels |
| `fillWidth` | `boolean` | `true` | See **`fillWidth` vs `layoutColumns`** section below |
| `layoutColumns` | `"equal" \| string[]` | `"equal"` | Controls per-child ` | ` width distribution. See below. |
### Visual
| Property | Type | Example | Description |
|---|---|---|---|
| `padding` | `string` | `"16px 24px"` | Inner padding applied inside the border wrapper |
| `backgroundColor` | `string` | `"#f9fafb"` | Background fill color |
| `borderRadius` | `string` | `"8px"` | Rounds all corners (also clips background) |
| `border` | `BorderConfig` | See below | Border on the outer wrapper table |
| `backgroundImage` | `BackgroundImageType` | See below | Background image for the row |
### Interactivity
| Property | Type | Description |
|---|---|---|
| `innerLink` | `IInnerLink` | Wraps the entire row in an `` tag. See **Link Support** section. |
### Mobile
| Property | Type | Description |
|---|---|---|
| `mobile.wrap` | `boolean` | When `true`, children stack vertically on mobile. Gap is injected as a spacer between stacked children. |
| `mobile.justifyContent` | `"start" \| "center" \| "end"` | Overrides `justifyContent` on mobile |
| `mobile.alignItems` | `"start" \| "center" \| "end"` | Overrides `alignItems` on mobile |
---
## `fillWidth` vs `layoutColumns` — Critical Distinction
These two properties both control how the content table expands, but they serve different purposes and can be combined.
### `fillWidth`
Controls whether the inner content table uses `width: 100%` or `width: auto`.
| `fillWidth` | Content table width | Use case |
|---|---|---|
| `false` / `undefined` (default) | `width: auto` | Children shrink-wrap to natural size. Use for icon rows, button rows, social links. |
| `true` | `width: 100%` | Children get a constrained box — text can wrap correctly in Outlook. Use for rows mixing text + images. |
### `layoutColumns`
Controls how each child ` | ` is sized. When set, also forces `table-layout: fixed` and `width: 100%` on the content table (regardless of `fillWidth`).
| Value | Behavior |
|---|---|
| `undefined` (default) | No width is set on child ` | `s — original shrink-wrap behavior |
| `"equal"` | Each child gets `${100 / numChildren}%`. Works for any number of children. |
| `string[]` | Explicit width per child (px, %, or mixed). Array **must** match children count exactly. |
```json
// Equal: 3 children each get 33.33%
"layoutColumns": "equal"
// Manual: explicit per-child widths
"layoutColumns": ["200px", "1fr", "80px"]
// Mixed units are allowed — author is responsible for not overflowing
"layoutColumns": ["60%", "40%"]
```
> **Note:** When using `%` values in `layoutColumns`, ensure they total ≤ 100% after accounting for any `gap`. Overflow behavior is undefined and client-dependent — the same trade-off accepted by MJML and Foundation for Emails.
### Decision Guide
| Scenario | `fillWidth` | `layoutColumns` |
|---|---|---|
| Icon row, button row, social links | `false` | `undefined` |
| Text + image side by side | `true` | `undefined` |
| Equal-width columns (e.g. 3 product cards) | either | `"equal"` |
| Asymmetric columns (e.g. 70/30 split) | either | `["70%", "30%"]` |
| Fixed sidebar + fluid content | either | `["200px", "auto"]` |
---
## `BorderConfig`
Specify either a **full border** (all sides) or **individual sides**. Do not mix both.
```json
// Full border — all sides
"border": {
"width": "1px",
"style": "solid",
"color": "#e5e7eb"
}
// Per-side — other sides are explicitly set to "none" (Outlook-safe)
"border": {
"left": { "width": "4px", "style": "solid", "color": "#6366f1" }
}
```
Supported side keys: `"top"`, `"right"`, `"bottom"`, `"left"`.
Each side requires all three of: `width`, `style`, `color`.
---
## `backgroundImage`
```json
"backgroundImage": {
"src": "https://example.com/texture.jpg",
"repeat": "no-repeat",
"size": "cover",
"position": "center center"
}
```
| Property | Options |
|---|---|
| `repeat` | `"no-repeat"`, `"repeat"`, `"repeat-x"`, `"repeat-y"` |
| `size` | `"auto"`, `"cover"`, `"contain"` |
| `position` | Any valid CSS `background-position` string |
Always pair with `backgroundColor` as a fallback for email clients that block images.
---
## Link Support (`innerLink`)
When `innerLink` is set (and not `devMode`), the entire row is wrapped in an `` tag with `display: block`. The link is stripped in dev mode.
```json
"innerLink": {
"type": "url",
"url": "https://example.com",
"target": "_blank"
}
```
| `type` | Required field | Rendered href |
|---|---|---|
| `"url"` | `url` | The URL as-is |
| `"email"` | `email` | `mailto:{email}` |
| `"phone"` | `phone` | `tel:{phone}` |
| `"anchor"` | `anchor` | `#{anchor}` |
| `"page_top"` | — | `#` |
| `"page_bottom"` | — | `#bottom` |
| `"none"` | — | No link rendered |
`target` defaults to `"_blank"` if not specified.
---
## Mobile Stacking
When `mobile.wrap: true` and there are 2+ children, the Row uses the same CSS-class-based stacking pattern as `Container` — compatible with Gmail's `@media` stripping.
- Each child ` | ` gets the `stack-td` class → forces `display: block; width: 100%` on mobile
- Gap columns get `desktop-gap-column` → collapsed on mobile
- A `mobile-gap-spacer` div is injected inside each child (except the last) → visible only on mobile, provides vertical spacing equal to `gap`
```json
"config": {
"gap": "16px",
"mobile": { "wrap": true }
}
```
> **Important:** `mobile.wrap` only activates stacking behavior. The `gap` value controls both horizontal gap (desktop) and vertical gap (mobile stack). If you want different spacing per breakpoint, that is not currently supported — one `gap` value serves both.
---
## React Usage
```tsx
import Row, { RowConfig } from './Row';
const config: RowConfig = {
gap: '16px',
justifyContent: 'center',
alignItems: 'center',
fillWidth: false,
};
```
The component is memoized via `arePropsEqual`. Pass stable `config` objects to avoid re-renders.
---
## Row vs Container — Key Differences
| | `Row` | `Container` |
|---|---|---|
| **Primary job** | Arrange children horizontally with alignment + gap | Control email column layout and fixed vs. full width |
| **Width control** | Fills parent column (`width: 100%` default) | `"fixed"` (e.g. `600px`, centered) or `"full"` |
| **Column sizing** | `layoutColumns` (`"equal"` or `string[]`) | `childrenConstraints` (`"equals"`, `"ratio"`, `"manual"`) |
| **`fillWidth`** | ✓ Yes — shrink-wrap vs. full-width content table | ✗ No |
| **Link wrapping** | ✓ Yes — `innerLink` wraps the whole row in `` | ✗ No |
| **Mobile stacking** | ✓ Yes — `mobile.wrap` | ✓ Yes — `shouldWrap` |
| **Border radius** | ✓ Yes | ✓ Yes |
| **Background image** | ✓ Yes | ✓ Yes |
| **`justifyContent`** | ✓ Yes — aligns children within the row | ✓ Yes — aligns the container table within its parent |
---
## Common Patterns & Examples
### 1. Icon/social link row (default shrink-wrap)
```json
{
"id": "social-row",
"type": "RowComponent",
"config": {
"gap": "12px",
"justifyContent": "center",
"alignItems": "center"
},
"children": []
}
```
No `fillWidth`, no `layoutColumns` — children naturally size to their content.
---
### 2. Text + image side by side (fillWidth for Outlook wrapping)
```json
{
"id": "text-image-row",
"type": "RowComponent",
"config": {
"gap": "24px",
"alignItems": "center",
"fillWidth": true,
"layoutColumns": ["60%", "40%"],
"mobile": { "wrap": true }
},
"children": []
}
```
---
### 3. Equal-width product cards (3-up)
```json
{
"id": "product-row",
"type": "RowComponent",
"config": {
"gap": "16px",
"justifyContent": "center",
"layoutColumns": "equal",
"mobile": { "wrap": true }
},
"children": []
}
```
---
### 4. Single centered button
```json
{
"id": "cta-row",
"type": "RowComponent",
"config": {
"justifyContent": "center",
"padding": "8px 0"
},
"children": []
}
```
No gap needed for a single child. No `layoutColumns` — button sizes to its natural width.
---
### 5. Clickable row (entire row is a link)
```json
{
"id": "link-row",
"type": "RowComponent",
"config": {
"gap": "12px",
"alignItems": "center",
"fillWidth": true,
"backgroundColor": "#f3f4f6",
"padding": "16px",
"borderRadius": "8px",
"innerLink": {
"type": "url",
"url": "https://example.com/article",
"target": "_blank"
}
},
"children": []
}
```
---
### 6. Accent left-border row (callout style)
```json
{
"id": "callout-row",
"type": "RowComponent",
"config": {
"fillWidth": true,
"padding": "16px 20px",
"backgroundColor": "#f0f9ff",
"border": {
"left": { "width": "4px", "style": "solid", "color": "#0ea5e9" }
}
},
"children": []
}
```
---
### 7. Fixed-height banner row with background image
```json
{
"id": "banner-row",
"type": "RowComponent",
"config": {
"height": "200px",
"justifyContent": "center",
"alignItems": "center",
"backgroundColor": "#0f172a",
"backgroundImage": {
"src": "https://example.com/banner.jpg",
"repeat": "no-repeat",
"size": "cover",
"position": "center center"
}
},
"children": []
}
```
---
## Data Bindings
`Row` supports the `bindings` property on the `ComponentNode`, alongside `id`, `type`, and `config`. Bindings connect this component to the Data Layer for dynamic rendering.
```json
{
"id": "social-row",
"type": "RowComponent",
"bindings": {
"dataList": "social_links",
"itemAlias": "link",
"visible": "user.showSocial === true",
"propertyMap": {
"config.backgroundColor": "link.brandColor"
}
},
"config": {
"gap": "12px",
"justifyContent": "center",
"alignItems": "center"
},
"children": []
}
```
| Property | Description |
|---|---|
| `dataList` | Dot-path to an array in the Data Layer. Makes `Row` a repeater — one row rendered per item. |
| `itemAlias` | Name used by descendant components to reference the current iteration item (e.g. `"link"` → `"link.brandColor"`). |
| `visible` | JS expression evaluated against the Data Layer. Hides the entire row when falsy. |
| `propertyMap` | Maps `config` property paths to Data Layer paths (e.g. `"config.backgroundColor"` → `"link.brandColor"`). |
---
## Rules & Constraints
- **`layoutColumns` array must match children count exactly.** If lengths differ, widths are silently not applied (undefined fallback).
- **`layoutColumns` forces `table-layout: fixed` and `width: 100%`** on the content table, overriding `fillWidth`. You cannot have `layoutColumns` without a full-width content table.
- **`gap` doubles as mobile spacer height** when `mobile.wrap: true`. A single value serves both orientations.
- **`innerLink` is suppressed in `devMode`.** The row renders without the `` wrapper when dev mode is active.
- **`borderRadius` clips backgrounds.** Setting `borderRadius` automatically applies `overflow: hidden` to the outer background ` | `, clipping background colors and images within rounded corners.
- **Per-side borders set all other sides to `"none"`** explicitly to prevent Outlook Classic from rendering phantom black borders. Do not rely on default/inherited border values when using per-side borders.
- **`height` is propagated to all nested tables** via both the `height` CSS property and the HTML `height` attribute for Outlook Classic compatibility.
- **`mobile.wrap` requires 2+ children** to have any effect. A single-child row is never stacked.
--- SECTION: IMAGE ---
**Image Component — LLM Reference**
`Image` renders a responsive, email-client-compatible image with optional linking. It uses a table wrapper for maximum compatibility across email clients (including Outlook) and supports desktop/mobile overrides, data bindings, and Outlook-specific pixel constraints.
---
## Position in the Document Tree
```
DocumentTree
└── Section
└── Container
└── Column
└── Text
└── Image ← can be linked or standalone
└── Spacer
└── Button
```
`Image` is a leaf node. It can be placed anywhere a child component is accepted (inside `Column`, `Row`, `Container`, etc.).
---
## ComponentNode JSON Structure
```json
{
"id": "unique-string-id",
"type": "ImageComponent",
"config": {
"src": "https://example.com/image.jpg",
"alt": "Alt text description",
"width": "100%",
"maxWidth": "600px"
}
}
```
---
## `ImageConfig` — Full Property Reference
| Property | Type | Required | Default | Description |
|-----------------------|-------------------------------|----------|-------------|-----------|
| `src` | `string` | Yes | — | Image source URL |
| `alt` | `string` | Yes | — | Alt text (required for accessibility) |
| `width` | `string` | No | "100%" | Desktop width (e.g. "100%", "300px") |
| `height` | `string` | No | "auto" | Desktop height |
| `maxWidth` | `string` | No | "100%" | Maximum width |
| `maxHeight` | `string` | No | — | Maximum height |
| `backgroundColor` | `string` | No | — | Background color of wrapper cell |
| `padding` | `string` | No | — | Padding around image (CSS string) |
| `borderRadius` | `string` | No | "0" | Border radius |
| `border` | `BorderConfig` | No | — | Border configuration |
| `innerLink` | `IInnerLink` | No | — | Preferred linking object |
| `href` | `string` | No | — | Deprecated. Use `innerLink` instead |
| `objectFit` | `"contain" \| "cover" \| ...` | No | — | CSS `object-fit` |
| `objectPosition` | `string` | No | — | CSS `object-position` |
| `outlookWidth` | `string` | No | — | Pixel width for Outlook Classic (no "px") |
| `mobile` | `ImageMobileConfig` | No | — | Mobile-specific overrides |
---
## `innerLink` Structure
```json
"innerLink": {
"type": "url" | "email" | "phone" | "anchor" | "page_top" | "page_bottom" | "none",
"url"?: string,
"email"?: string,
"phone"?: string,
"anchor"?: string,
"target"?: "_blank" | "_self"
}
```
---
## Mobile Overrides (`mobile`)
Only explicitly set properties are overridden on mobile. Unspecified properties inherit desktop values.
```json
"mobile": {
"width": "100%",
"maxWidth": "100%",
"borderRadius": "0px",
"hidden": false
}
```
---
## How It Works
The component renders inside a `` for email compatibility. It includes:
- Desktop styles + mobile media query (`@media (max-width: 768px)`)
- Outlook Classic support via MSO conditional comments when `outlookWidth` is set
- Automatic link wrapping when `innerLink` is provided
- `display: block` on the ` ` to remove unwanted gaps
- Unique class names for safe mobile CSS targeting
---
## React Usage
```tsx
import Image, { ImageConfig } from './Image';
const config: ImageConfig = {
src: "https://example.com/image.jpg",
alt: "Product showcase",
width: "100%",
maxWidth: "600px"
};
```
The component is memoized via `arePropsEqual`.
---
## Common Patterns & Examples
### 1. Basic responsive image
```json
{
"id": "img_hero",
"type": "ImageComponent",
"config": {
"src": "https://cdn.example.com/hero.jpg",
"alt": "Hero banner",
"width": "100%",
"maxWidth": "600px"
}
}
```
### 2. Linked image
```json
{
"id": "cta_image",
"type": "ImageComponent",
"config": {
"src": "https://example.com/button.jpg",
"alt": "Shop Now",
"innerLink": {
"type": "url",
"url": "https://example.com/shop",
"target": "_blank"
},
"outlookWidth": "600"
}
}
```
### 3. Mobile-optimized image
```json
{
"id": "product_img",
"type": "ImageComponent",
"config": {
"src": "https://example.com/product.jpg",
"alt": "Product",
"width": "280px",
"mobile": {
"width": "100%",
"maxWidth": "100%"
}
}
}
```
### 4. Data-bound image (inside repeater)
```json
{
"id": "dynamic_img",
"type": "ImageComponent",
"config": {
"src": "",
"alt": ""
},
"bindings": {
"propertyMap": {
"config.src": "item.image_url",
"config.alt": "item.name"
}
}
}
```
---
## Data Bindings
`Image` supports the `bindings` property on the `ComponentNode`, alongside `id`, `type`, and `config`. Leaf components do not support `dataList` or `itemAlias` — use those on a parent `Container`, `Column`, or `Row` to repeat the image. `visible` and `propertyMap` are fully supported.
```json
{
"id": "dynamic-img",
"type": "ImageComponent",
"bindings": {
"visible": "item.hasImage === true",
"propertyMap": {
"config.src": "item.imageUrl",
"config.alt": "item.name"
}
},
"config": {
"src": "",
"alt": "",
"width": "100%",
"maxWidth": "600px"
}
}
```
| Property | Description |
|---|---|
| `visible` | JS expression evaluated against the Data Layer. Hides this component when falsy. |
| `propertyMap` | Maps component property paths (prefixed with `config.`) to Data Layer paths. |
> Bindable properties include `config.src`, `config.alt`, `config.width`, `config.maxWidth`, `config.backgroundColor`, and any other scalar `ImageConfig` field. `innerLink` itself cannot be bound via `propertyMap` — repeat the image inside a `Container` with `dataList` to vary links per item.
---
## Rules & Constraints
- `src` and `alt` are **required**.
- Prefer `innerLink` over the deprecated `href`/`target`.
- Use `outlookWidth` (numeric string, e.g. `"600"`) when you need reliable fixed-width rendering in Outlook Classic.
- `mobile` properties are **overrides only** — they do not reset desktop values unless explicitly set.
- Always provide meaningful `alt` text.
- For completely hidden images on mobile, use `mobile: { "hidden": true }`.
- Non-pixel values in `width`/`height` are handled gracefully but `px` or `%` are recommended for predictability.
--- SECTION: ICON ---
**Icon Component — LLM Reference**
`Icon` renders a dynamically generated icon using the Iconify API. It supports color, size, rotation, background, borders, and clickable links while maintaining excellent email client compatibility (including Outlook via VML).
---
## Position in the Document Tree
```
DocumentTree
└── Section
└── Container
└── Column
└── Text
└── Icon ← inline or standalone icon
└── Spacer
└── Button
```
`Icon` is a leaf node. It can be placed anywhere a child component is accepted.
---
## ComponentNode JSON Structure
```json
{
"id": "unique-string-id",
"type": "IconComponent",
"config": {
"iconIdentifier": "mdi:arrow-right",
"width": "24",
"color": "#000000"
}
}
```
---
## `IconConfig` — Full Property Reference
| Property | Type | Required | Default | Description |
|-----------------------|-------------------------------------------|----------|-------------|-----------|
| `iconIdentifier` | `string` | Yes | — | Iconify icon name (e.g. `"mdi:home"`, `"logos:react"`) |
| `width` | `string \| number` | No | 24 | Icon width (px) |
| `height` | `string \| number` | No | 24 | Icon height (px) |
| `rotate` | `number` | No | 0 | Rotation angle in degrees |
| `rotateOrientation` | `"cw" \| "ccw"` | No | `"cw"` | Rotation direction |
| `color` | `string` | No | `"#000000"` | Icon color (hex recommended) |
| `innerLink` | `IInnerLink` | No | — | Makes icon clickable |
| `backgroundColor` | `string` | No | — | Background color of container |
| `padding` | `string` | No | `"0"` | Padding around icon |
| `borderRadius` | `string` | No | `"0"` | Border radius of container |
| `border` | `BorderConfig` | No | — | Border configuration |
| `justifyContent` | `"start" \| "center" \| "end"` | No | `"center"` | Horizontal alignment |
---
## `innerLink` Structure
```json
"innerLink": {
"type": "url" | "email" | "phone" | "anchor" | "page_top" | "page_bottom" | "none",
"url"?: string,
"email"?: string,
"phone"?: string,
"anchor"?: string,
"target"?: "_blank" | "_self"
}
```
---
## How It Works
- Icons are generated at runtime via the Iconify API as PNG images.
- Uses a nested table structure for email compatibility.
- When `backgroundColor` + `borderRadius` are used, VML (``) is injected for Outlook Classic support.
- `display: block`, `fontSize: 0`, and `lineHeight: 0` are used to eliminate unwanted spacing.
- Alignment is handled via the `align` attribute on the outer table.
---
## React Usage
```tsx
import Icon, { IconConfig } from './Icon';
const config: IconConfig = {
iconIdentifier: "mdi:check-circle",
width: 32,
color: "#22c55e",
justifyContent: "center"
};
```
The component is memoized via `arePropsEqual`.
---
## Common Patterns & Examples
### 1. Simple icon
```json
{
"id": "icon_check",
"type": "IconComponent",
"config": {
"iconIdentifier": "mdi:check",
"width": 24,
"color": "#10b981"
}
}
```
### 2. Clickable icon (linked)
```json
{
"id": "icon_link",
"type": "IconComponent",
"config": {
"iconIdentifier": "mdi:arrow-right",
"width": 20,
"color": "#3b82f6",
"innerLink": {
"type": "url",
"url": "https://example.com",
"target": "_blank"
}
}
}
```
### 3. Icon with background and border radius
```json
{
"id": "icon_bg",
"type": "IconComponent",
"config": {
"iconIdentifier": "mdi:star",
"width": 28,
"color": "#ffffff",
"backgroundColor": "#eab308",
"padding": "8px",
"borderRadius": "9999px"
}
}
```
### 4. Rotated icon
```json
{
"id": "icon_rotate",
"type": "IconComponent",
"config": {
"iconIdentifier": "mdi:refresh",
"width": 24,
"rotate": 45,
"color": "#64748b"
}
}
```
### 5. Right-aligned icon
```json
{
"id": "icon_align",
"type": "IconComponent",
"config": {
"iconIdentifier": "mdi:chevron-right",
"width": 18,
"color": "#94a3b8",
"justifyContent": "end"
}
}
```
---
## Data Bindings
`Icon` supports the `bindings` property on the `ComponentNode`, alongside `id`, `type`, and `config`. Leaf components do not support `dataList` or `itemAlias` — use those on a parent `Container`, `Column`, or `Row` to repeat the component. `visible` and `propertyMap` are fully supported.
```json
{
"id": "dynamic-icon",
"type": "IconComponent",
"bindings": {
"visible": "item.hasIcon === true",
"propertyMap": {
"config.iconIdentifier": "item.iconName",
"config.color": "item.iconColor"
}
},
"config": {
"iconIdentifier": "mdi:check-circle",
"width": 24,
"color": "#10b981"
}
}
```
| Property | Description |
|---|---|
| `visible` | JS expression evaluated against the Data Layer. Hides this component when falsy. |
| `propertyMap` | Maps component property paths (prefixed with `config.`) to Data Layer paths. |
> Bindable properties include `config.iconIdentifier`, `config.color`, `config.backgroundColor`, `config.width`, `config.height`, and any other scalar `IconConfig` field.
---
## Rules & Constraints
- `iconIdentifier` is **required** — without it the component renders nothing.
- Use full Iconify format: `"{collection}:{icon-name}"` (e.g. `mdi:home`, `logos:figma`, `ion:heart`).
- Width and height should be numbers or pixel strings (e.g. `24` or `"24px"`).
- Hex colors (`#rrggbb`) are recommended for best compatibility.
- `borderRadius` + `backgroundColor` together trigger VML for Outlook rounded backgrounds.
- The icon is always rendered with `object-fit: contain`.
- `alt` text is intentionally empty (`""`) as icons are decorative in most email use cases.
--- SECTION: HTML ---
**Html Component — LLM Reference**
The `Html` component is the absolute root of the email document. It wraps the entire tree, provides the necessary XML namespaces for legacy Outlook (VML) support, sets the document language, and defines the global background color that fills the email client's viewport.
---
## Position in the Document Tree
`Html` is the **root component**. It is the first tag in the generated output and contains exactly two primary children: the `Head` and the `Body`.
```tsx
{/* Content */}
```
---
## ComponentNode JSON Structure
```json
{
"id": "root_html",
"type": "HtmlComponent",
"config": {
"backgroundColor": "#ffffff",
"lang": "en"
},
"children": [
{ "id": "head_1", "type": "HeadComponent", "config": { ... } },
{ "id": "body_1", "type": "BodyComponent", "config": { ... } }
]
}
```
---
## `HtmlConfig` — Full Property Reference
| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `backgroundColor` | `string` | No | `"#ffffff"` | The background color of the entire email client canvas (viewport). |
| `lang` | `string` | No | `"en"` | Sets the `lang` and `xml:lang` attributes for accessibility. |
| `children` | `ComponentNode[]` | Yes | — | Must contain a `Head` and a `Body` component. |
---
## How It Works
* **Namespace Injection:** Automatically adds `xmlns:v` and `xmlns:o` to the `` tag. This is critical for rendering vector shapes (VML) and specific office-based fixes in Outlook 2003-2019.
* **Legacy Backgrounds:** Uses the `bgcolor` attribute on the `` tag. While modern web dev uses CSS, many email clients (especially older Outlook versions) rely on the `bgcolor` attribute on the root to render the canvas color correctly.
* **React Implementation:** Uses `React.createElement` internally to bypass JSX validation rules that occasionally flag colon-namespaced attributes (like `xmlns:v`).
---
## React Usage
```tsx
import Html from './Html';
import Head from './Head';
import Body from './Body';
{/* Email Rows */}
```
---
## Common Patterns & Examples
### 1. Minimal Root Structure
The standard entry point for every email generated by the builder.
```json
{
"id": "email_root",
"type": "HtmlComponent",
"config": {
"backgroundColor": "#ffffff"
},
"children": [
{ "type": "HeadComponent", "id": "h", "config": {} },
{ "type": "BodyComponent", "id": "b", "config": {} }
]
}
```
### 2. Dark Mode Preview Background
Setting a dark global background for high-contrast or themed templates.
```json
{
"id": "email_root",
"type": "HtmlComponent",
"config": {
"backgroundColor": "#1a1a1a",
"lang": "en"
},
"children": [...]
}
```
---
## Rules & Constraints
* **Strict Hierarchy:** An `Html` node must be the top-most node in the JSON `DocumentTree`.
* **Required Children:** It should always contain exactly one `Head` and one `Body`.
* **Accessibility:** Always ensure the `lang` property matches the primary language of the email content for screen reader compatibility.
* **Background Sync:** For the best visual experience, the `backgroundColor` of the `Html` component should either match the `Body` background or be a complementary "outer" color (e.g., a light gray `Html` background with a white `Body` container).
--- SECTION: HEADING ---
**Heading Component — LLM Reference**
`Heading` renders a semantic HTML heading (`` to ``) with full text styling support. It is optimized for email clients with a table wrapper for reliable padding, background, and alignment.
---
## Position in the Document Tree
```
DocumentTree
└── Section
└── Container
└── Column
└── Heading ← semantic heading (h1–h6)
└── Spacer
└── Text
```
`Heading` is a leaf node. It can be placed anywhere a child component is accepted.
---
## ComponentNode JSON Structure
```json
{
"id": "unique-string-id",
"type": "HeadingComponent",
"config": {
"text": "Welcome to our newsletter",
"level": "h1",
"color": "#1f2937",
"fontSize": "32px"
}
}
```
---
## `HeadingConfig` — Full Property Reference
| Property | Type | Required | Default | Description |
|--------------------|-----------------------------------|----------|-----------|-----------|
| `text` | `string` (HTML) | No | — | Heading content. Can include inline HTML. |
| `level` | `"h1" \| "h2" \| "h3" \| "h4" \| "h5" \| "h6"` | No | `"h1"` | Semantic HTML heading level |
| `padding` | `string` | No | — | Padding around the heading (e.g. `"20px 0"`) |
| `color` | `string` | No | — | Text color (hex recommended) |
| `textAlign` | `"left" \| "center" \| "right" \| "justify"` | No | — | Horizontal alignment |
| `fontFamily` | `string` | No | — | Font family stack |
| `fontSize` | `string` | No | — | Font size (e.g. `"28px"`) |
| `fontWeight` | `string` | No | — | Font weight (`"400"`, `"700"`, `"bold"`, etc.) |
| `fontStyle` | `string` | No | — | `"italic"` or `"normal"` |
| `lineHeight` | `string` | No | — | Line height (e.g. `"1.3"` or `"34px"`) |
| `letterSpacing` | `string` | No | — | Letter spacing (e.g. `"0.5px"`) |
| `textTransform` | `string` | No | — | `"uppercase"`, `"capitalize"`, etc. |
| `textDecoration` | `string` | No | — | `"underline"`, `"line-through"` |
| `backgroundColor` | `string` | No | — | Background color of the block |
| `verticalAlign` | `string` | No | `"top"` | Vertical alignment in the cell |
| `wordBreak` | `string` | No | — | Word break behavior |
| `whiteSpace` | `string` | No | — | White space handling |
---
## How It Works
- Renders inside a `` → `| ` wrapper for consistent padding, background, and width control across email clients.
- Default browser margins on heading tags are reset (`margin: 0`).
- Supports rich HTML content in `text` with automatic link style injection.
- Uses inline styles for maximum email client compatibility.
- `msoLineHeightRule: exactly` is applied for better Outlook rendering.
---
## React Usage
```tsx
import Heading, { HeadingConfig } from './Heading';
const config: HeadingConfig = {
text: "Our Latest Updates",
level: "h2",
fontSize: "28px",
color: "#111827",
textAlign: "center"
};
```
The component is memoized via `arePropsEqual`.
---
## Common Patterns & Examples
### 1. Main hero heading
```json
{
"id": "hero_heading",
"type": "HeadingComponent",
"config": {
"text": "Summer Sale is Here!",
"level": "h1",
"fontSize": "36px",
"color": "#1f2937",
"textAlign": "center",
"padding": "20px 0 12px 0"
}
}
```
### 2. Section heading
```json
{
"id": "section_title",
"type": "HeadingComponent",
"config": {
"text": "Featured Products",
"level": "h2",
"fontSize": "24px",
"fontWeight": "700",
"color": "#374151",
"padding": "32px 0 16px 0"
}
}
```
### 3. Centered heading with background
```json
{
"id": "bg_heading",
"type": "HeadingComponent",
"config": {
"text": "Special Offer",
"level": "h3",
"fontSize": "22px",
"color": "#ffffff",
"backgroundColor": "#3b82f6",
"textAlign": "center",
"padding": "16px 0",
"fontWeight": "600"
}
}
```
### 4. Heading with custom line height and letter spacing
```json
{
"id": "tight_heading",
"type": "HeadingComponent",
"config": {
"text": "Limited Time Deal",
"level": "h2",
"fontSize": "30px",
"lineHeight": "1.1",
"letterSpacing": "-0.5px",
"fontWeight": "800"
}
}
```
---
## Data Bindings
`Heading` supports the `bindings` property on the `ComponentNode`, alongside `id`, `type`, and `config`. Leaf components do not support `dataList` or `itemAlias` — use those on a parent `Container`, `Column`, or `Row` to repeat the component. `visible` and `propertyMap` are fully supported.
```json
{
"id": "dynamic-heading",
"type": "HeadingComponent",
"bindings": {
"visible": "section.showTitle === true",
"propertyMap": {
"config.text": "section.title",
"config.color": "section.titleColor"
}
},
"config": {
"text": "Fallback Heading",
"level": "h2",
"fontSize": "28px",
"color": "#1f2937"
}
}
```
| Property | Description |
|---|---|
| `visible` | JS expression evaluated against the Data Layer. Hides this component when falsy. |
| `propertyMap` | Maps component property paths (prefixed with `config.`) to Data Layer paths. |
> Bindable properties include `config.text`, `config.color`, `config.backgroundColor`, `config.fontSize`, `config.fontFamily`, `config.padding`, and any other scalar `HeadingConfig` field.
---
## Rules & Constraints
- Use the `text` property to set content. It supports basic HTML (e.g. ``, ``, ``).
- `level` should always be specified for semantic correctness.
- Always define `fontSize` explicitly — do not rely on default browser sizes for headings in email.
- `padding` and `backgroundColor` are applied to the wrapping table cell.
- Default top/bottom margins are removed — use `padding` for spacing control.
- For very complex HTML content, consider using the `Text` component instead.
--- SECTION: HEAD ---
**Head Component — LLM Reference**
`Head` generates the `` section of the email document. It includes essential meta tags, global CSS resets, mobile responsiveness rules, Outlook fixes, and font declarations.
---
## Position in the Document Tree
`Head` is a **top-level** component and must be placed directly inside the email root (not in the body).
```tsx
{/* Fonts + custom styles */}
```
---
## ComponentNode JSON Structure
```json
{
"id": "head",
"type": "HeadComponent",
"config": {
"title": "Monthly Newsletter",
"backgroundColor": "#ffffff",
"rowGaps": ["8px", "16px", "32px", "48px"]
}
}
```
---
## `HeadConfig` — Full Property Reference
| Property | Type | Required | Default | Description |
|--------------------|-----------------------|----------|----------------------|-----------|
| `title` | `string` | No | `"Email Preview"` | Email subject / browser title |
| `backgroundColor` | `string` | No | `"#ffffff"` | Global email background color |
| `rowGaps` | `string[]` | No | `[]` | Gap values used for mobile responsive spacing |
| `fonts` | `ResolvedFont[]` | No | `[]` | Auto-resolved fonts (from builder pipeline) |
| `children` | `ReactNode` | No | — | Additional custom ` | | | | | |