**Site index:** [https://2plot.dev/llms.txt](https://2plot.dev/llms.txt) — every page on this site, as Markdown.  
**Network index:** [https://2plot.ai/llms.txt](https://2plot.ai/llms.txt) — The 2plot network; start here to discover sibling sites.  
**Sibling sites:** 11 more in The 2plot network — listed in the site index above.  
**Sitemap:** https://2plot.dev/sitemap.xml  

> **Full documentation:** [https://email.2plot.dev](https://email.2plot.dev) — the dedicated dash-email documentation site, with the complete API reference and deeper examples. This page is the quick-start overview.



### Installation

[Visit GitHub Repo](https://github.com/pip-install-python/dash-email) · [PyPI](https://pypi.org/project/dash-email/)

```bash
pip install dash-email
```

### Introduction

`dash-email` bridges Python-first development with the component patterns pioneered by [React Email](https://react.email). Build email templates with familiar Dash syntax, preview them live inside your app, and export inline-styled, table-based HTML that renders correctly in Gmail, Outlook, Apple Mail, and every other major client.

*   **15 email-safe components** — structure, layout, typography, buttons, images, fonts.
*   **Email-client compatible by construction** — rows and columns render as real HTML tables; all styling is inlined.
*   **First-class Dash citizens** — every component takes an `id` and works with callbacks like any other Dash component.
*   **Send & schedule** — integrated [Resend](https://resend.com) support for single sends, batches up to 100 recipients, and scheduling.

```python
import dash_email as de
```

> Status: **early release (0.0.x)**. The component API is stable; the AI builder and sending utilities are actively evolving.

---

### Quick Start

A complete welcome email: `Email` → `EmailBody` → `EmailContainer` → `EmailSection`, with a heading, paragraph, and a bulletproof CTA button. What you see below is the live component tree rendered in the browser — the exact same tree converts to email-safe HTML at send time.



```python
# File: docs/dash_email/quick_start.py

import dash_email as de

component = de.Email(
    lang="en",
    children=[
        de.EmailBody(
            style={"backgroundColor": "#f6f9fc", "padding": "40px 0",
                   "borderRadius": "12px"},
            children=[
                de.EmailContainer([
                    de.EmailSection(
                        style={
                            "backgroundColor": "#ffffff",
                            "borderRadius": "8px",
                            "padding": "40px",
                        },
                        children=[
                            de.EmailHeading(
                                "Welcome to 2plot.dev!",
                                as_="h1",
                                style={"color": "#1a1a1a", "marginBottom": "16px"},
                            ),
                            de.EmailText(
                                "Thanks for creating an account. You now have access to "
                                "every live component example, full API references, and "
                                "the AI documentation assistant.",
                                style={"color": "#666666", "lineHeight": "1.6"},
                            ),
                            de.EmailButton(
                                "Explore the docs",
                                href="https://2plot.dev",
                                style={
                                    "backgroundColor": "#12B886",
                                    "color": "#ffffff",
                                    "padding": "12px 24px",
                                    "borderRadius": "4px",
                                    "fontWeight": "bold",
                                },
                            ),
                        ],
                    )
                ])
            ],
        )
    ],
)
```


---

### Rows & Columns

Email clients are not browsers — floats, flexbox, and grid are unreliable or stripped entirely. `EmailRow` / `EmailColumn` are the layout primitives that survive every client: rows render as HTML tables and columns as table cells. Set widths with percentage styles on each column.



```python
# File: docs/dash_email/rows_columns.py

import dash_email as de

# Emails are always light-themed artifacts: every text element carries an
# explicit color so the preview renders identically on the docs site's
# light AND dark modes (unstyled text would inherit the page theme color).
CELL = {"padding": "12px", "verticalAlign": "top"}
HEADING = {"color": "#1a1a1a"}
BODY_TEXT = {"color": "#666666", "lineHeight": "1.6"}

component = de.Email([
    de.EmailBody(
        style={
            "backgroundColor": "#f6f9fc",
            "padding": "32px 0",
            "borderRadius": "12px",
        },
        children=[
            de.EmailContainer([
                de.EmailSection(
                    style={
                        "backgroundColor": "#ffffff",
                        "borderRadius": "8px",
                        "padding": "24px",
                    },
                    children=[
                        de.EmailHeading("Two columns", as_="h3", style=HEADING),
                        de.EmailRow([
                            de.EmailColumn(
                                style={"width": "50%", **CELL},
                                children=[
                                    de.EmailText(
                                        "Left column — rows and columns render "
                                        "as real HTML tables, the only layout "
                                        "primitive every email client supports.",
                                        style=BODY_TEXT,
                                    )
                                ],
                            ),
                            de.EmailColumn(
                                style={"width": "50%", **CELL},
                                children=[
                                    de.EmailText(
                                        "Right column — set widths with "
                                        "percentage styles on each column.",
                                        style=BODY_TEXT,
                                    )
                                ],
                            ),
                        ]),
                        de.EmailDivider(style={"margin": "16px 0"}),
                        de.EmailHeading("70 / 30 split", as_="h3", style=HEADING),
                        de.EmailRow([
                            de.EmailColumn(
                                style={"width": "70%", **CELL},
                                children=[
                                    de.EmailText("dash-email Pro license",
                                                 style=BODY_TEXT)
                                ],
                            ),
                            de.EmailColumn(
                                style={"width": "30%", "textAlign": "right", **CELL},
                                children=[
                                    de.EmailText("$99.00", style=BODY_TEXT)
                                ],
                            ),
                        ]),
                    ],
                )
            ])
        ],
    )
])
```


---

### Buttons & Links

`EmailButton` is a "bulletproof" call-to-action — a styled anchor that keeps its padding and background across clients. `EmailLink` is its inline counterpart for links inside running text.



```python
# File: docs/dash_email/button_cta.py

import dash_email as de

component = de.Email([
    de.EmailBody(
        style={"backgroundColor": "#f6f9fc", "padding": "32px 0",
               "borderRadius": "12px"},
        children=[
            de.EmailContainer([
                de.EmailSection(
                    style={
                        "backgroundColor": "#ffffff",
                        "borderRadius": "8px",
                        "padding": "32px",
                        "textAlign": "center",
                    },
                    children=[
                        de.EmailHeading(
                            "New release: dash-leaflet2",
                            as_="h2",
                            style={"color": "#1a1a1a"},
                        ),
                        de.EmailText(
                            "Leaflet 2-native maps for Dash — 26 components, no "
                            "react-leaflet dependency. Read the announcement, or "
                            "jump straight into the interactive docs.",
                            style={"color": "#666666", "lineHeight": "1.6"},
                        ),
                        de.EmailButton(
                            "Read the docs",
                            href="https://2plot.dev/pip/dash_leaflet2",
                            style={
                                "backgroundColor": "#228be6",
                                "color": "#ffffff",
                                "padding": "12px 28px",
                                "borderRadius": "6px",
                                "fontWeight": "bold",
                                "marginRight": "8px",
                            },
                        ),
                        de.EmailText(
                            [
                                "Prefer video? Watch the walkthrough on ",
                                de.EmailLink(
                                    "YouTube @2plotai",
                                    href="https://www.youtube.com/@2plotai",
                                    style={"color": "#12B886"},
                                ),
                                ".",
                            ],
                            style={"color": "#999999", "fontSize": "13px"},
                        ),
                    ],
                )
            ])
        ],
    )
])
```


---

### The styling boundary

Email clients strip `<style>` tags and ignore most modern CSS, so every dash-email component takes a `style` dict of camelCase CSS that renders **inline** on the element.

Rules of thumb:

*   Keep content at **600px or less** — `EmailContainer` enforces this for you.
*   Multi-column layout goes through `EmailRow` / `EmailColumn` — never flexbox.
*   Prefer web-safe fonts, or load one via `EmailFont` with a `fallbackFontFamily`.
*   Always set explicit `width` / `height` + `alt` on `EmailImage`.

```python
de.EmailRow([
    de.EmailColumn(style={"width": "70%"}, children=[de.EmailText("Product")]),
    de.EmailColumn(style={"width": "30%", "textAlign": "right"}, children=[de.EmailText("$99.00")]),
])
```

---

### Sending with Resend

Templates built with these components convert to email-safe HTML and send through the [Resend](https://resend.com) API — single sends, batches up to 100 recipients, and scheduled delivery (up to 30 days ahead). Set `RESEND_API_KEY` in your environment:

```python
from utils.email_sender import send_email, component_to_html

html = component_to_html(my_email_component)
result = send_email(
    to_email="user@example.com",
    subject="Welcome!",
    html_content=html,
    from_email="hello@yourdomain.com",
    scheduled_at="in 1 hour",   # optional — ISO 8601 or natural language
)
```

This documentation site uses exactly this pipeline for its own transactional and announcement emails.

---

### Components

| Component        | Category  | What it is                                                        |
|:-----------------|:----------|:------------------------------------------------------------------|
| `Email`          | Structure | Root wrapper for the email template                               |
| `EmailHead`      | Structure | Metadata container (font imports, etc.)                           |
| `EmailPreview`   | Structure | Inbox preview text — visible next to the subject, hidden in body  |
| `EmailBody`      | Structure | Main content wrapper                                              |
| `EmailContainer` | Layout    | Centered container at the 600px email standard                    |
| `EmailSection`   | Layout    | Groups related content with its own background/padding            |
| `EmailRow`       | Layout    | Horizontal row — renders as an HTML `<table>`                     |
| `EmailColumn`    | Layout    | Column within a row — renders as a `<td>`                         |
| `EmailHeading`   | Content   | Headings `h1`–`h6` via the `as_` prop                             |
| `EmailText`      | Content   | Paragraph text                                                    |
| `EmailButton`    | Content   | Bulletproof call-to-action button (styled anchor)                 |
| `EmailLink`      | Content   | Inline hyperlink                                                  |
| `EmailImage`     | Media     | Image with explicit dimensions                                    |
| `EmailDivider`   | Media     | Horizontal rule separator                                         |
| `EmailFont`      | Media     | Web font loading with graceful fallback                           |

### Component Properties

| Property | Type | Default | Description |
|:---------|:-----|:--------|:------------|
| id | string | — | Dash callback identity — available on every component |
| style | dict | — | camelCase CSS, rendered inline for email-client safety |
| children | node | — | Child components / text |
| as_ | string | "h1" | `EmailHeading` level: `"h1"`–`"h6"` (Python-safe alias for `as`) |
| href | string | — | `EmailButton` / `EmailLink` target URL |
| src | string | — | `EmailImage` source URL |
| alt | string | — | `EmailImage` alt text |
| width / height | string \| number | — | `EmailImage` explicit dimensions |
| lang | string | "en" | `Email` document language |
| fontFamily | string | — | `EmailFont` font family name |
| fallbackFontFamily | string | — | `EmailFont` web-safe fallback |
| webFont | object | — | `EmailFont` web font source configuration |
