smail/email
Write HTML compliant emails with peace of mind, like Lustre ๐.
smail is a library for composing HTML emails in Gleam. It handles all
the quirks of email client rendering (Outlook MSO, Yahoo, AOL, Apple
Mail, etc.) so you can focus on content rather than compatibility.
If you need something not covered by this module โ such as a plain table
layout or inline <span> elements โ the lower-level
smail/html module is available, though you will
have to handle email client compatibility yourself.
Once your email is composed, use the rendering functions to produce the final output:
to_htmlโ renders the element tree to an HTML string ready to be sent as the body of an HTML email.to_plain_textโ renders the element tree to a plain text fallback for email clients that do not support HTML.
Types
A CSS generic or named fallback font family.
pub type FallbackFont {
Arial
Helvetica
Verdana
Georgia
TimesNewRoman
Serif
SansSerif
Monospace
Cursive
Fantasy
CustomFallback(String)
}
Constructors
-
Arial -
Helvetica -
Verdana -
Georgia -
TimesNewRoman -
Serif -
SansSerif -
Monospace -
Cursive -
Fantasy -
CustomFallback(String)
The format of a web font file, used in the src: declaration of @font-face.
pub type FontFormat {
Woff
Woff2
TrueType
OpenType
EmbeddedOpenType
Svg
}
Constructors
-
Woff -
Woff2 -
TrueType -
OpenType -
EmbeddedOpenType -
Svg
pub type FontParams {
FontParams
}
Constructors
-
FontParams
The CSS font-style value for a web font.
pub type FontStyle {
StyleNormal
StyleItalic
StyleOblique
}
Constructors
-
StyleNormal -
StyleItalic -
StyleOblique
The CSS font-weight value for a web font.
pub type FontWeight {
WeightNormal
WeightBold
WeightBolder
WeightLighter
Weight100
Weight200
Weight300
Weight400
Weight500
Weight600
Weight700
Weight800
Weight900
}
Constructors
-
WeightNormal -
WeightBold -
WeightBolder -
WeightLighter -
Weight100 -
Weight200 -
Weight300 -
Weight400 -
Weight500 -
Weight600 -
Weight700 -
Weight800 -
Weight900
pub type PlainTextConfig {
PlainTextConfig(wrap_column: Int, enable_wrapping: Bool)
}
Constructors
-
PlainTextConfig(wrap_column: Int, enable_wrapping: Bool)
A remote web font to load via @font-face.
pub type WebFont {
WebFont(url: String, format: FontFormat)
}
Constructors
-
WebFont(url: String, format: FontFormat)
Values
pub fn advanced_to_plain_text(
el: html.Element,
config: PlainTextConfig,
) -> String
Like to_plain_text but accepts a PlainTextConfig to
control the column at which lines are wrapped and whether wrapping is
enabled at all.
Example
import smail/email
import smail/html
email.html([], [
email.head([], []),
email.body([], [html.text("Hello!")]),
])
|> email.advanced_to_plain_text(email.PlainTextConfig(
wrap_column: 80,
enable_wrapping: True,
))
pub fn body(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render the email <body> element with cross-client compatibility fixes.
Example
import smail/email
import smail/html
import smail/style
email.body(
[style.background_color("#f6f6f6"), style.padding("20px 0")],
[html.text("Email content here")],
)
pub fn button(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render a clickable button compatible with Outlook (MSO) and modern email clients.
Example
import smail/attribute
import smail/email
import smail/html
import smail/style
email.button(
[
style.background_color("#000"),
style.color("#fff"),
style.padding("12px 24px"),
attribute.href("https://example.com"),
],
[html.text("Click me")],
)
pub fn center(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render a horizontally centered wrapper.
Example
import smail/attribute
import smail/email
import smail/html
import smail/style
email.center([], [
email.button(
[
style.background_color("#000"),
style.padding("12px 24px"),
attribute.href("https://example.com"),
],
[html.text("Get started")],
),
])
pub fn column(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render a column cell inside a row.
Example
import smail/attribute
import smail/email
import smail/html
email.row([], [
email.column([attribute.width("50%")], [
html.text("Left"),
]),
email.column([attribute.width("50%")], [
html.text("Right"),
]),
])
pub fn container(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render a centered email container with a maximum width of 600 px (37.5 em).
Example
import smail/email
import smail/html
email.body([], [
email.container([], [
html.text("Centered content, max 600px wide"),
]),
])
pub fn font(
family font_family: String,
fallback_family fallback_font_family: List(FallbackFont),
web_font web_font: option.Option(WebFont),
weight font_weight: option.Option(FontWeight),
style font_style: option.Option(FontStyle),
) -> html.Element
Generate a <style> element that declares a custom web font and applies it globally.
Example
import gleam/option
import smail/email
email.font(
family: "Josefin Sans",
fallback_family: [email.Verdana, email.SansSerif],
web_font: option.Some(email.WebFont(
url: "https://fonts.gstatic.com/s/josefinsans/v32/Qw3aZQNVED7rKGKxtqIqX5EUDXx4.woff2",
format: email.Woff2,
)),
weight: option.None,
style: option.None,
)
pub fn h1(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render an <h1> heading with email-safe default styles.
pub fn h2(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render an <h2> heading with email-safe default styles.
pub fn h3(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render an <h3> heading with email-safe default styles.
pub fn head(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render the <head> element with email-required meta tags prepended.
Automatically inserts two meta tags before any provided children:
Content-Type: text/html; charset=UTF-8โ ensures correct character encoding.x-apple-disable-message-reformattingโ prevents Apple Mail from resizing or reformatting the email layout on iOS devices.
Example
import smail/attribute
import smail/email
import smail/html
email.head([], [
html.meta([
attribute.name("color-scheme"),
attribute.content("light"),
]),
])
pub fn hr(attrs: List(attribute.Attribute)) -> html.Element
Render a horizontal rule <hr> styled as a thin separator line.
Example
import smail/email
import smail/html
email.section([], [
email.paragraph([], [html.text("Above the line")]),
email.hr([]),
email.paragraph([], [html.text("Below the line")]),
])
pub fn html(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render the root <html> element.
Thin wrapper around smail/html.html.
Example
import smail/email
email.html([], [
email.head([], []),
email.body([], []),
])
pub fn img(attrs: List(attribute.Attribute)) -> html.Element
Render an <img> element with email-safe default styles.
Example
import smail/attribute
import smail/email
email.img([
attribute.src("https://example.com/logo.png"),
attribute.alt("Company logo"),
attribute.width(200),
])
pub fn link(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render an <a> link with email-safe default styles.
Example
import smail/attribute
import smail/email
import smail/html
email.link(
[attribute.href("https://example.com")],
[html.text("Visit our website")],
)
pub fn paragraph(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render a paragraph <p> with email-safe default typography.
Example
import smail/email
import smail/html
email.paragraph([], [
html.text("Your order has been confirmed."),
])
pub fn preview(text: String) -> html.Element
Render a hidden preview text node shown in email client inbox previews.
Example
import smail/email
email.html([], [
email.head([], []),
email.body([], [
email.preview("Your order #1234 has been shipped!"),
email.container([], []),
]),
])
pub fn row(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render a horizontal row used for multi-column layouts.
Example
import smail/email
import smail/html
email.row([], [
email.column([], [html.text("Left column")]),
email.column([], [html.text("Right column")]),
])
pub fn section(
attrs: List(attribute.Attribute),
children: List(html.Element),
) -> html.Element
Render a full-width layout section.
Example
import smail/email
import smail/html
email.container([], [
email.section([], [
email.paragraph([], [html.text("First section")]),
]),
email.section([], [
email.paragraph([], [html.text("Second section")]),
]),
])
pub fn to_html(el: html.Element) -> String
Converts an element to an HTML email.
Example
import smail/email
import smail/html
email.html([], [
email.head([], []),
email.body([], [html.text("Hello!")]),
])
|> email.to_html
pub fn to_plain_text(el: html.Element) -> String
Renders an element tree to a plain text string. Lines are wrapped at 72 characters by default.
Use advanced_to_plain_text to customise the
wrapping behaviour.
Example
import smail/email
import smail/html
email.html([], [
email.head([], []),
email.body([], [html.text("Hello!")]),
])
|> email.to_plain_text