One Source of Truth
for Every Email Template.
Design Once. Export to Anything.
Maildeno is a headless email template builder for developers and their teams. Design beautiful templates visually, define merge tag logic and visibility rules, debug them with a visual logic debugger — then export to HTML, MJML, or React Email and send with any ESP you already use. No lock-in. No migration cost. No scattered template files.
The Workflow
One template. Three output formats, any ESP.
Visual logic debugger. See exactly what renders given any data.
Local rendering. Your customer data never hits our servers.
Let's Be Clear
About What This Is.
If you've landed here wondering whether this is just another email marketing tool or a competitor to your ESP — it's not. Here's exactly what Maildeno is and isn't.
Not an ESP
Maildeno doesn't send emails. Use AWS SES, Postmark, Resend, Mailgun — whatever you already have. We just make the templates.
Not a marketing tool
Built for developers and engineering teams shipping transactional email at scale, not marketers building newsletters in a drag-and-drop.
Not another messy HTML exporter
The output is clean, production-ready HTML, MJML, or React Email — not the bloated inline-everything soup most template builders produce.
Not vendor lock-in
Switch ESPs without touching a single template. Export format is yours — store it in your repo, deploy it however you want.
A headless template builder
Design in a visual editor. Pull templates programmatically via SDK. Render locally with your own data at send time.
The source of truth for your email templates
One place where every template lives, is versioned, and is owned — not scattered across ESPs, codebases, and Figma files.
A designer-developer bridge
Designers build in the visual editor. Developers integrate via SDK. No handoff friction. No "can you just make this match the mockup" tickets.
A logic layer for dynamic email
Merge tags, visibility rules, and a visual logic debugger — so the template itself owns the conditional logic, not scattered if-statements in your backend.
Works with every ESP you already use
Two Roles.
One Workflow.
Maildeno has a clear separation between the person designing the template and the developer integrating it. Both sides get the right tools. Neither has to wait on the other.
Build in the visual editor
Drag, drop, and nest rows and columns into a layout. Design with the aesthetic quality you'd expect from a modern design tool — not a 2015 enterprise editor. No code required.
Define merge tags
Insert dynamic placeholders directly into the template — customer name, order total, CTA link. Tag them as text, URL, or attribute so the SDK knows how to escape them correctly.
Set visibility rules
Attach show/hide conditions to rows or sections. Rules are driven by context values passed at render time — "show this row only when plan = pro" — without any code changes.
Debug with the visual logic debugger
Pass a context object directly in the editor and see exactly what the template renders to. No more guessing whether your visibility rule fired. No console logs. No trial-and-error.
Publish
When the template is ready, publish it. The SDK picks up the new version on the next TTL cycle automatically — no redeploy required.
An Email Editor That
Doesn't Embarrass You.
Most email builders produce visually mediocre output and fight you every step of the way. Maildeno's editor is built around high visual quality — with a developer-first export layer designed for modern workflows, not workarounds.
Nested rows and columns
Build complex multi-column layouts with full nesting support. Not just two-column side-by-sides — proper nested structure that renders correctly across every email client.
Dynamic layout structure
Design sections that show, hide, or rearrange based on context. The layout isn't static HTML — it responds to the data you pass at render time.
Aesthetic quality that matches your brand
Typography controls, spacing precision, gradient support, custom borders. Your transactional emails don't have to look like they were built in 2012.
Merge tag insertion inline
Insert merge tags directly where they belong in the design — names, URLs, amounts — without switching to a code view. Type-tagged so the SDK escapes them correctly.
Visibility rule builder
Attach show/hide conditions to any row or section through a UI — no JSON editing, no code. Set conditions like "plan === pro" or "daysLeft < 7" visually.
Visual logic debugger
The standout feature. Enter a context object directly in the editor and preview the template exactly as it will render for that specific user. No guessing. No deploys to test.
Export Formats
Fully inlined CSS. Maximum email client compatibility. Production-ready.
Best for: AWS SES, Resend, Postmark, Mailgun, any SMTP
Clean, readable source. Compiles to bulletproof responsive HTML. Version-controllable.
Best for: Teams who want template source in version control
Type-safe TSX components. Full TypeScript support. Works with react-email renderer.
Best for: Next.js, Remix, React-based stacks
Everything You Need.
Nothing You Don't.
A focused feature set built around the real friction in email template workflows — design quality, dynamic logic, format flexibility, and developer ergonomics.
Visual Logic Debugger
Enter a context object directly in the editor and see exactly what the template renders to. Which rows show, which hide, which merge tags resolve. No deploy. No console log. No guessing.
Nested Row & Column Layout
Build complex email layouts with full nesting support. Not just two-column grids — proper nested structure that renders correctly across Gmail, Outlook, Apple Mail, and every major client.
Visibility Rules
Attach show/hide conditions to any row or section visually. Rules are evaluated against context values at render time — logic lives in the template, not scattered across your backend.
Typed Merge Tags
Three variants: text (HTML-escaped), url (percent-encoded), attr (attribute-safe). The SDK applies the correct escaping automatically — no XSS risk, no manual encoding.
Multi-Format Export
One template, three output targets: HTML, React Email, MJML. Change the target parameter and get a completely different output — same template, same data, different consumers.
Local Wasm Rendering
An embedded WebAssembly engine renders templates in-process. Merge tags and context are applied locally — your customer data is never sent to Maildeno's servers.
Built-in Caching
Memory and disk cache with configurable TTL. Templates are fetched once per window — every subsequent render is pure CPU. Stale-on-error fallback keeps sends running even if Maildeno is unreachable.
Scoped API Keys
Create keys restricted to specific render targets. A key with targets: ["html"] returns 403 FORBIDDEN on MJML or React Email requests. Enforce what each environment can do.
Typed SDKs — JS & Python
Full TypeScript types for JS. PEP 561 hints for Python. Sync and async clients. Single runtime dependency each. Works in Express, NestJS, Next.js, FastAPI, Django, Flask, Lambda, and more.
Built for Teams
That Ship Email at Scale.
Whether you're a SaaS team drowning in scattered templates, an agency juggling client projects, or a designer-developer team fighting over handoff — Maildeno gives everyone the right tool for their role.
Pain Points
- Templates scattered across your ESP editor, a repo of MJML files, and a Figma doc nobody keeps in sync
- Every ESP migration means rewriting all your templates from scratch
- Engineering is bottlenecked making small copy changes that a designer or PM could do themselves
How Maildeno Helps
One source of truth
Every template lives in Maildeno. Export to your ESP whenever you need. Switch providers without touching template code.
Non-engineers can edit safely
Designers and PMs edit templates in the visual editor. Developers get clean output via SDK. No one steps on each other.
Logic lives in the template
Visibility rules and merge tags are defined in the editor. No more scattered if-statements across your backend for every email variation.
Templates Fetched Once.
Rendered Forever. Locally.
The SDK resolves every render call through a layered cache before ever touching the network. Your dynamic data is processed by an embedded Wasm engine — it never leaves your server.
Cache Resolution Sequence
Memory Cache
In-process heap
Fastest path. If the template was fetched this session, rendering is pure CPU — zero I/O, zero latency. Zero configuration required.
Disk Cache
Filesystem persist
Opt-in for serverless. Survives cold starts and process restarts. Shared across all workers pointing at the same directory.
Maildeno Server
Template JSON fetch
The network call happens at most once per template per TTL window. After the first fetch, it never reaches the network again until TTL expires.
Wasm Render Engine
In-process, every time
An embedded WebAssembly engine renders merge tags and applies context visibility rules locally. Your dynamic data is never sent to Maildeno's servers.
Maildeno Goes Down.
Your Emails Don't.
The SDK never throws when a cached copy exists. On TTL expiry + unreachable server, it falls back silently and flags fromStaleCache so you can log the event without blocking sends.
const result = await client.render({
templateId: "550e8400-...",
target: "html",
})
// The SDK NEVER throws when a cache entry exists.
// fromStaleCache = true when Maildeno was unreachable
// and the SDK fell back to the last known-good copy.
if (result.fromStaleCache) {
logger.warn("Rendered from stale cache", {
templateId: result.templateId,
})
}
// result.output is always valid — send it.
await emailProvider.send({
to: user.email,
subject: "Welcome!",
html: result.output,
}) Memory or Disk.
You Choose.
Both types share the same API. Switch by changing a single config key. Disk cache persists across Lambda cold starts and Gunicorn worker restarts.
Best for: Long-lived servers (Express, NestJS, FastAPI)
+Zero config, sub-millisecond reads
−Lost on process exit, per-worker independent
Best for: Serverless (Lambda, Cloud Run, Vercel Functions)
+Survives restarts, shared across workers
−Requires writable filesystem path
// Memory (default) — zero config
new MaildenoClient({ apiKey: "sk_live_..." })
// Disk — survives cold starts
new MaildenoClient({
apiKey: "sk_live_...",
cache: {
type: "disk",
path: "/tmp/maildeno-cache",
ttl: 300_000, // 5 min
maxEntries: 50,
},
})Reliability Guarantees
Stale-on-Error Fallback
When the Maildeno server is unreachable and a cached copy exists, the SDK renders from the last known-good entry and sets `result.fromStaleCache = true`. It never throws. Your send pipeline continues uninterrupted.
Throw Only on Cold Miss
The SDK only throws when the server is unreachable AND no prior cache entry exists at all — i.e. first-ever render with no cache. In every other scenario, you get output.
Configurable TTL
Control how long a cached template is considered fresh. After TTL expires a background re-fetch is attempted. Default is 5 minutes — adjust per workload.
Integrate in Minutes.
Ship Faster.
Official SDKs for JavaScript / TypeScript and Python. Same concepts, native idioms. Built-in caching, Wasm rendering, and stale-on-error fallback out of the box.
JavaScript / TypeScript
npm install maildenoPython
pip install maildenoimport { MaildenoClient } from "maildeno"
const client = new MaildenoClient({
apiKey: process.env.MAILDENO_API_KEY,
// timeout: 30_000 (ms, default)
})
const html = await client.renderHtml("550e8400-e29b-41d4-a716-446655440000")
const tsx = await client.renderReact("template-id")
const mjml = await client.renderMjml("template-id")
console.log(html) // <!DOCTYPE html>...
// Full control via render()
const result = await client.render({
templateId: "template-id",
target: "html", // "html" | "react-email" | "mjml"
})
console.log(result.output, result.target, result.templateId)Works in every environment
Stop Scattering Templates
Across Every ESP.
One place to design, define logic, and export to any format. Your send infrastructure stays exactly as it is.
Free tier available. No credit card required. Works with every ESP. Your customer data never leaves your server.
Sign Up Free
Start building templates immediately. No credit card. No commitment. Full access from day one.
See the Editor
Try the visual editor, build a template, and see what the output looks like. Takes 5 minutes.
Integrate the SDK
Install the JS or Python package, point it at your template, and render to HTML, MJML, or React Email in your first call.
Send with your ESP
Pass the output directly to AWS SES, Postmark, Resend, Mailgun — whatever you already use. We don't touch the send.