DEV Community

Cover image for I open-sourced the Notion Email rendering engine from my SaaS
강상권
강상권

Posted on

I open-sourced the Notion Email rendering engine from my SaaS

I've been running notionto.email, a service that
sends Notion pages as emails. The hardest part wasn't the Notion API or email
delivery. It was the rendering layer.

So I extracted it and open-sourced it: notion-to-email.

TL;DR

notion-to-email is a TypeScript library that turns any Notion page into
email-compatible HTML, ready to pass to SES, SendGrid, Resend, or Nodemailer.

import { renderFromNotion } from 'notion-to-email'

const { html, title } = await renderFromNotion({
  pageId: 'your-page-id',
  token: 'your-notion-token',
})
// Pass html to SES, SendGrid, Nodemailer, Resend, etc.
Enter fullscreen mode Exit fullscreen mode

The rendering problem

Email HTML is notoriously finicky. No Flexbox. No Grid. Table-based layouts,
inline styles, and every client renders things slightly differently.

Notion's private images also use short-lived signed URLs, so you need to
re-host them before sending or they'll be broken by the time someone opens
the email.

Installation

npm install notion-to-email @notionhq/client
Enter fullscreen mode Exit fullscreen mode

What's supported

20+ block types, all rendered to table-based email HTML:

  • Paragraphs, Headings H1–H4
  • Bulleted & Numbered Lists
  • To-Do, Toggle, Quote, Callout
  • Code blocks, Equations (image fallback)
  • Tables, Column layouts
  • Images, YouTube thumbnails, File links, Bookmarks
  • Synced blocks, Child pages/databases, Link to page, Table of Contents
  • Rich text: bold, italic, strikethrough, inline code, colors, links, mentions

Configuration

await renderFromNotion({
  pageId: 'your-page-id',
  token: 'your-token',
  options: {
    // Notion private images expire quickly. Re-host them before sending
    // so they're still visible when recipients open the email.
    // `context` contains { blockId, blockType, pageId, isPublicPage, usage }.
    // usage: 'image' | 'cover' | 'icon' | 'file'
    resolveImageUrl: (url, context) =>
      `https://your-cdn.com/proxy?url=${encodeURIComponent(url)}`,

    // Show a "View in Notion" button at the top of the email
    header: { showNotionButton: true },

    // Append a custom footer (raw HTML)
    footer: '<p>Sent via My App</p>',

    // 'placeholder' renders a visible fallback for unsupported blocks
    // instead of silently dropping them
    onUnsupportedBlock: 'placeholder',
  },
})
Enter fullscreen mode Exit fullscreen mode

CLI

npx notion-to-email <page-id> --token secret_xxx
npx notion-to-email <page-id> -o email.html
npx notion-to-email https://notion.so/My-Page-abc123
Enter fullscreen mode Exit fullscreen mode

GitHub: https://github.com/Sangkwun/notion-to-email
npm: https://www.npmjs.com/package/notion-to-email

MIT licensed. PRs and issues are always welcome.

Top comments (0)