everydayai-email-generator
v1.0.14
Published
A TypeScript wrapper for MJML to simplify email generation
Readme
Email Generator
A powerful TypeScript wrapper for MJML that simplifies professional email generation with a fluent API, standardised styling, and component-based architecture.
Table of Contents
- Email Generator
- Table of Contents
- Installation
- Quick Start
- API Reference
- Components
- Section Layout Components
- Header Component
- Text Section Component
- Image Component
- Metrics Section Component
- Table Section Component
- Bar Graph Component
- Bar List Component
- Report Intro Component
- Highlighted Section Component
- Gradient Section Component
- Download Section Component
- Hyperlink Button Component
- Fact Section Component
- Footnote Section Component
- Status Section Component
- Progress Bar Component
Installation
npm install everydayai-email-generatorQuick Start
The Email Generator provides a fluent API for building professional emails with consistent styling and responsive design.
import { createEmail } from 'everydayai-email-generator';
// Create a example email with multiple components
const email = createEmail()
.newSection()
.header('Monthly Report', 'Performance summary for December 2024')
.endSection()
.newSection()
.textSection('Thank you for your continued partnership. This is an example of a text section with an optional header included', 'Introduction')
.metricsSection('Key Performance Indicators', [
{ value: '$125,000', description: '+12% vs last month' },
{ value: '248', description: '+8% growth' },
])
.endSection()
.newSection()
.barGraphSection('Quarterly Comparison', [
{ label: 'Q1', value: 85 },
{ label: 'Q2', value: 92 },
{ label: 'Q3', value: 78 }
])
.barListSection('Updated Contact Fields', [
{ label: 'Phone Numbers', value: 5842 },
{ label: 'Email Addresses', value: 4231 },
{ label: 'Addresses', value: 3927 },
{ label: 'Names', value: 2814 },
{ label: 'Other Fields', value: 1642 }
], 'Scaled against 7,000 total updates.', 7000, true)
.progressBarSection('Annual Target Progress', 73, 'Goal: $500k by year end')
.endSection();
// Render to HTML
const result = email.render();
Result:

The resulting HTML can be used with any email service that accepts HTML content and paired with something like Nodemailer to distribute the email.
API Reference
Core Functions
createEmail()
Creates a new EmailBuilder instance with a fluent API for building emails. This is the starting point for building an email.
Returns: EmailBuilder
const email = createEmail();EmailBuilder Methods
All methods return the EmailBuilder instance for method chaining, enabling a fluent API pattern.
render(options?): RenderResult
Renders the email to HTML using MJML.
Parameters:
options(RenderOptions, optional): Rendering configuration (advanced usage)
Returns: RenderResult with html string and optional errors array
const result = email.render({ minify: true, beautify: false });getMjml(): string
Gets the raw MJML markup for the email without rendering to HTML.
Returns: string - The complete MJML document
const mjmlMarkup = email.getMjml();Components
Section Layout Components
Control the visual grouping and separation of email sections. All components are rendered with white rectangular backgrounds that can be chained together. To create different email sections using visual grouping for separation of content, you can use the newSection() and endSection() methods.
newSection(): EmailBuilder
Starts a new email section with rounded top corners and proper spacing.
Features:
- Creates visual separation from previous content/starts the email
- Rounded top corners only
- Proper margin and padding
- White background
endSection(): EmailBuilder
Ends an email section with rounded bottom corners and proper spacing.
Features:
- Creates visual separation for next content
- Rounded bottom corners only
- Proper margin and padding
- Complements
newSection()for grouped content
Example (With and without sectioning): Take this example of a simple email with no sectioning:
const email = createEmail()
.header('Here\'s some components with no sectioning', 'This is a subtitle')
.textSection('This is a text section with an optional header included', 'Introduction')
.metricsSection('Key Performance Indicators', [
{ value: '$125,000', description: '+12% vs last month' },
{ value: '248', description: '+8% growth' },
])
.textSection('This is a text section explaining the bargraph without an optional header included')
.barGraphSection('Quarterly Comparison', [
{ label: 'Q1', value: 85 },
{ label: 'Q2', value: 92 },
{ label: 'Q3', value: 78 }
])No Sectioning Visual Example:

And now with sectioning:
const email = createEmail()
.newSection()
.header('Here\'s some components with no sectioning', 'This is a subtitle')
.endSection()
.newSection()
.textSection('This is a text section with an optional header included', 'Introduction')
.endSection()
.newSection()
.metricsSection('Key Performance Indicators', [
{ value: '$125,000', description: '+12% vs last month' },
{ value: '248', description: '+8% growth' },
])
.endSection()
.newSection()
.textSection('This is a text section explaining the bargraph without an optional header included')
.barGraphSection('Quarterly Comparison', [
{ label: 'Q1', value: 85 },
{ label: 'Q2', value: 92 },
{ label: 'Q3', value: 78 }
])
.endSection()Sectioning Visual Example:

Header Component
Creates a prominent header section with title and optional subtitle.
header(title: string, subtitle?: string): EmailBuilder
Parameters:
title(string): The main header text (required)subtitle(string, optional): Optional subtitle displayed below the title
Features:
- Large, bold title text
- Left-aligned layout
- Optional subtitle with smaller font
- Consistent spacing and styling
Example:
const email = createEmail()
.newSection()
.header('Monthly Performance Report', '2025 Q2')
.endSection()
Visual Example:

Text Section Component
Creates a content section with formatted text and optional title.
textSection(content: string, title?: string): EmailBuilder
Parameters:
content(string): The main text content (required)title(string, optional): Optional section title displayed above content
Features:
- Readable typography with optimal line height
- HTML escaping for security
- Optional section titles
- Responsive text layout
Example:
const email = createEmail()
.newSection()
.textSection('Thank you for your continued partnership with our services.')
.textSection('Our team has been working hard to improve...', 'Updates')
.endSection()Visual Example:

Image Component
Creates an embedded image block from a publicly accessible URL, with optional title and subtitle text above the image.
imageComponent(imageUrl: string, options?: ImageComponentOptions): EmailBuilder
Parameters:
imageUrl(string): Publicly accessible image URL to embed (required)options(ImageComponentOptions, optional): Optional configuration object Includestitle,subtitle,altText, andwidth
Features:
- Gmail-friendly hosted image embedding
- Optional title and subtitle above the image
- Full-width responsive image rendering by default
- Useful for charts, screenshots, and report graphics
Example:
const email = createEmail()
.newSection()
.imageComponent(
'https://storage.googleapis.com/logo-storage-eai/performanceGraphRw.png',
{
title: 'Data Quality Protection',
altText: 'Performance graph showing maintained quality over time',
}
)
.endSection()Visual Example:

Metrics Section Component
Creates a dashboard-style metrics display with cards in a responsive grid layout.
metricsSection(title: string, metrics: MetricItem[], subtitle?: string, variant?: MetricsSectionVariant): EmailBuilder
Parameters:
title(string): The main section title (required)metrics(MetricItem[]): Array of metric objects (required)subtitle(string, optional): Optional section subtitlevariant('outline' | 'filled', optional): Card style variant. Defaults to'outline'
Features:
- Responsive grid layout
- Card-based design with rounded corners
- Automatic 2-column layout for standard metric sets
- Automatic 3-column layout when the metric count is divisible by 3
- Cards align flush with the section width while preserving internal spacing
- Optional filled cream card variant for summary-style metric rows
- Support for metric labels, values, and descriptions
- Consistent spacing and typography
Example:
Standard 2-column outline cards:
const email = createEmail()
.newSection()
.metricsSection('Initial Data Reset Results', [
{ value: '47,892', description: 'Records Processed' },
{ value: '18,456', description: 'Data Inconsistencies Corrected' },
{ value: '6,214', description: 'Duplicates Resolved' },
{ value: '38%', description: 'Records Corrected' }
])
.endSection()3-up filled cards:
const email = createEmail()
.newSection()
.metricsSection('Impact Summary', [
{ value: '247', description: 'Hours Saved (Initial Reset)' },
{ value: '$8,645', description: 'Cost Equivalent Avoided' },
{ value: '41,678', description: 'Clean Records' }
], undefined, 'filled')
.endSection()Visual Example:

Table Section Component
Creates a professional data table with headers and rows.
tableSection(title: string, tableData: TableData, subtitle?: string): EmailBuilder
Parameters:
title(string): The main section title (required)tableData(TableData): Object containing headers and rows (required)subtitle(string, optional): Optional section subtitle
Features:
- Responsive table design
- Header row with distinct styling
- Mobile-friendly layout
Example:
const email = createEmail()
.newSection()
.tableSection('Sales Performance', {
headers: ['Region', 'Sales', 'Growth'],
rows: [
{ cells: ['North', '$45,000', '+12%'] },
{ cells: ['South', '$38,000', '+8%'] },
{ cells: ['East', '$52,000', '+15%'] },
{ cells: ['West', '$41,000', '+6%'] }
]
}, 'Regional breakdown')
.endSection()Visual Example:

Bar Graph Component
Creates a visual bar chart with up to 3 bars using company brand colours.
barGraphSection(title: string, bars: BarData[], subtitle?: string): EmailBuilder
Parameters:
title(string): The main section title (required)bars(BarData[]): Array of bar data objects, maximum 3 bars (required)subtitle(string, optional): Optional section subtitle
Features:
- Automatic scaling relative to largest value
- Company brand colours (blue, green, orange)
- Responsive bar sizing
- Label and value display
- Maximum of 3 bars for optimal readability
Example:
const email = createEmail()
.newSection()
.barGraphSection('Quarterly Comparison', [
{ label: 'Q1', value: 85, subtitle: 'Strong start' },
{ label: 'Q2', value: 92, subtitle: 'Peak performance' },
{ label: 'Q3', value: 78, subtitle: 'Seasonal dip' }
], 'Performance by quarter')
.endSection()Visual Example:

Bar List Component
Creates a horizontal bar list with labels on the left, values aligned on the right, and each bar scaled against either an explicit total or the largest supplied value.
barListSection(title: string, bars: BarListData[], subtitle?: string, total?: number, contained?: boolean): EmailBuilder
Parameters:
title(string): The section titlebars(BarListData[]): The horizontal bar items to rendersubtitle(string, optional): Optional supporting copy above the barstotal(number, optional): Explicit total used to scale all bars. If omitted, the largest value becomes the full-width bar.contained(boolean, optional): Renders the title and bar rows inside a cream card, with each row using an inline label, bar, and value layout.
Features:
- Right-aligned amount labels for every row
- Uses the same brand palette as the vertical bar graph, plus yellow and black when more colours are needed
- Supports custom display values and custom colours per row
- Falls back to max-value scaling when no total is provided
- Optional contained card layout for denser comparison rows
Example:
const email = createEmail()
.newSection()
.barListSection('Contact Fields Updated', [
{ label: 'Phone Numbers', value: 5842 },
{ label: 'Email Addresses', value: 4231 },
{ label: 'Addresses', value: 3927 },
{ label: 'Names', value: 2814 },
{ label: 'Other Fields', value: 1642, colour: '#000000' }
], 'Bars scaled against a total of 18,456 updates.', 18456)
.barListSection('Contact Fields Updated', [
{ label: 'Phone Numbers', value: 5842 },
{ label: 'Email Addresses', value: 4231 },
{ label: 'Addresses', value: 3927 },
{ label: 'Names', value: 2814 },
{ label: 'Other Fields', value: 1642 }
], 'No total supplied, so the largest value becomes the full-width bar.')
.barListSection('Contact Duplicates Merged', [
{ label: 'Last 6 mths', value: 156, colour: '#FF702D' },
{ label: 'Last 30 days', value: 45, colour: '#66CC55' },
{ label: 'This week', value: 12, colour: '#0FB0EE' }
], undefined, undefined, true)
.endSection()Visual Example:

Report Intro Component
Creates a modular report opening block with a main title, optional support header, blue separator, and up to three follow-up text rows.
reportIntro(title: string, options?: ReportIntroOptions): EmailBuilder
Parameters:
title(string): The main report titleoptions(ReportIntroOptions, optional): Optional supporting content fields
Options:
subtitle(string, optional): Secondary heading below the titleprimaryLine(string, optional): First black line under the separatormetadataItems(string[], optional): Grey metadata line joined by a separatormetadataSeparator(string, optional): Separator used between metadata items. Defaults to•footerLine(string, optional): Final black line
Features:
- Large title and muted support header
- Rounded blue separator pill
- Clean options-object API so each line can be omitted independently
- Suitable for report identity, dates, versions, and context
Example:
const email = createEmail()
.newSection()
.reportIntro('Ray White AI Data Integrity Report', {
subtitle: 'Initial Data Reset',
primaryLine: '{franchise_name}',
metadataItems: ['October 1-31, 2025', 'Full Database Reset'],
footerLine: 'Eve {version_number}',
})
.endSection()Visual Example:

Highlighted Section Component
Creates a rounded stat callout with optional colour treatment and a right-aligned value.
highlightedSection(title: string, value?: string, tone?: HighlightedSectionTone, boldText?: boolean): EmailBuilder
Parameters:
title(string): The main stat labelvalue(string, optional): Optional value shown on the righttone('green' | 'yellow' | 'red' | 'neutral' | '', optional): Colour treatment. Use''or'neutral'for the cream version with black text.boldText(boolean, optional): Whether the label and value should be bold. Defaults totrue.
Features:
- Green, yellow, red, or neutral cream styling
- Right-aligned value layout
- Smaller default text size than the original callout version
- Optional non-bold rendering for neutral summary rows
- Text-only callouts automatically render at normal body text size
Example:
const email = createEmail()
.newSection()
.highlightedSection('Total Fields Updated', '18,456', '', false)
.highlightedSection('Total Duplicates Resolved', '6,214', 'green')
.highlightedSection('Incomplete Fields Standardised', '482', 'yellow')
.highlightedSection('High-Risk Records Flagged', '73', 'red')
.endSection()Visual Example:

Gradient Section Component
Creates a cream rounded insight card with a gradient progress bar, a movable black marker label, and supporting text above and below the bar.
gradientSection(markerLabel: string, percentage: number, topText: string, bottomText: string): EmailBuilder
Parameters:
markerLabel(string): Text shown inside the black marker pillpercentage(number): Marker position from1to100topText(string): Introductory copy inside the card above the barbottomText(string): Closing copy inside the card below the bar
Features:
- Orange-to-blue-to-green gradient bar
- Black marker label positioned from 1-100%
- Supports lightweight
**bold**text inside the top and bottom copy - Intentionally excludes title/subtitle so you can pair it with a normal header or text section above
- Useful for capture quality, completion quality, or confidence-style metrics
Example:
const email = createEmail()
.newSection()
.textSection('Contact Capture Quality', 'Trends and Insights')
.endSection()
.newSection()
.gradientSection(
'You',
72,
'Of contacts added or updated this week, **72% were fully captured** with a name, phone number, email, and linked property.',
"The remaining 28% are missing at least one field. Without a full set of details, it's harder to confidently match, merge, and enrich those records."
)
.endSection()Visual Example:

Download Section Component
Creates a bordered cream download prompt with explanatory copy and a single in-panel download/open button.
downloadSection(title: string, content: string, buttonLabel: string, buttonUrl: string, inlineButtonLabel?: string, inlineButtonUrl?: string): EmailBuilder
Parameters:
title(string): Section title shown inside the bordered panelcontent(string): Supporting explanation copy inside the panelbuttonLabel(string): Label for the in-panel buttonbuttonUrl(string): Destination URL for the in-panel buttoninlineButtonLabel(string, optional): Optional override label for the in-panel buttoninlineButtonUrl(string, optional): Optional override URL for the in-panel button
Features:
- Cream bordered panel for contextual guidance
- Single button rendered inside the panel
- Optional inline button args let you override the displayed CTA without changing the base method shape
- Supports lightweight
**bold**text inside the content copy
Example:
const email = createEmail()
.newSection()
.downloadSection(
'Possible Duplicates',
"How to use this list: Eve thinks some of these accounts might be duplicates but wasn't sure enough to update them. Could you please take a look and update any duplicates directly in Vault if you agree.",
'Download Duplicates (CSV)',
'https://example.com/downloads/duplicates.csv',
'Download CSV',
'https://example.com/downloads/duplicates.csv'
)
.endSection()Visual Example:

Hyperlink Button Component
Creates a clickable button that can be either full-width or compact right-aligned, with cream or black styling plus an optional metric-style bordered treatment.
hyperlinkButton(label: string, url: string, align?: 'full' | 'right', colour?: 'cream' | 'black', variant?: 'default' | 'metric', buttonImage?: 'downloadIcon'): EmailBuilder
Parameters:
label(string): The visible button texturl(string): The destination URL to open when clickedalign('full' | 'right', optional): Layout mode. Defaults to'full'colour('cream' | 'black', optional): Button colour style. Defaults to'cream'variant('default' | 'metric', optional): Visual treatment. Use'metric'for a white button with a thin cream border. Defaults to'default'buttonImage('downloadIcon', optional): Optional inline icon shown to the left of the label
Features:
- Full-width or compact right-aligned layout
- Cream or black button styling
- Optional metric-style white button with a thin cream border
- Optional inline download icon
- Rounded corners and clickable button body
- Useful for download links, spreadsheets, dashboards, and client resources
Example:
const email = createEmail()
.newSection()
.hyperlinkButton(
'Download All Changes (CSV)',
'https://example.com/downloads/all-changes.csv',
'full',
'cream',
'metric',
'downloadIcon'
)
.hyperlinkButton(
'Open Performance Spreadsheet',
'https://example.com/reports/performance-sheet',
'right',
'black'
)
.endSection()Visual Example:

Fact Section Component
Creates a cream callout for interesting context or interpretation, with an optional coloured accent on the left edge.
factSection(content: string, accentColour?: string): EmailBuilder
Parameters:
content(string): The fact or insight text to displayaccentColour(string, optional): Optional left accent colour. Supports brand colour names likeblue,green,yellow,orange,red, andblack, or any CSS colour string.
Features:
- Cream background that separates the text from white sections
- Optional left accent edge inside the rounded container
- Normal body text sizing for short callout paragraphs
- Useful for insight statements, takeaways, or contextual notes
Example:
const email = createEmail()
.newSection()
.factSection('Resolving duplicates eliminates confusion, prevents conflicting data, and ensures your team works with a single source of truth for each contact and property.')
.factSection('This baseline represents a fully reset system. Ongoing reports will focus on maintaining this standard as new data enters the database.', 'blue')
.endSection()Visual Example:

Footnote Section Component
Creates a light cream separator line followed by small muted supporting text.
footnoteSection(content: string): EmailBuilder
Parameters:
content(string): Footnote content. Supports line breaks and lightweight**bold**text.
Features:
- Thin cream separator line
- Small muted supporting copy
- Useful for assumptions, calculation notes, and reporting caveats
Example:
const email = createEmail()
.newSection()
.footnoteSection(
'* Hours saved: Based on 12,847 records × 1.15 min/record manual time\n* Cost saved: 247 hours × $35/hour average labor cost'
)
.endSection()Visual Example:

Status Section Component
Creates a cream status card with a coloured title dot and as many bullet points as needed.
statusSection(title: string, items: string[], tone?: StatusSectionTone): EmailBuilder
Parameters:
title(string): The status heading shown beside the coloured dotitems(string[]): Array of bullet point lines to render under the titletone('green' | 'yellow' | 'red', optional): Colour used for the status dot
Features:
- Cream card background matching the fact section
- Green, yellow, or red status dot
- Flexible bullet list length
- Clean layout for summary/status callouts
Example:
const email = createEmail()
.newSection()
.statusSection('Database Status', [
'All core datasets normalised and aligned',
'Duplicate and conflict risks contained',
'Contact data structured for ongoing use',
], 'green')
.statusSection('Risk Status', [
'Outstanding validation still required',
'Manual review queue remains active',
], 'yellow')
.endSection()Visual Example:

Progress Bar Component
Creates a visual progress indicator showing percentage completion.
progressBarSection(title: string, percentage: number, subtitle?: string, showPercentage?: boolean, variant?: 'default' | 'headline'): EmailBuilder
Parameters:
title(string): The main section title (required)percentage(number): Progress percentage from 0-100 (required)subtitle(string, optional): Optional section subtitleshowPercentage(boolean, optional): Shows inline percentage text in the default variant when usefulvariant('default' | 'headline', optional): Visual style. Use'headline'for the large centered percentage with a slim black bar
Features:
- Orange fill colour matching brand
- Rounded rectangle design
- Percentage validation (0-100)
- Smooth visual progression
- Clear percentage display
- Optional headline variant with centered large percentage and black slim bar
Example:
const email = createEmail()
.newSection()
.progressBarSection('Annual Target Progress', 73)
.progressBarSection(
'Neighbourhood Ownership Coverage',
34,
'Based on your active patch, you hold contact details for approximately 34% of property owners.',
true,
'headline'
)
.endSection()Visual Example:

