This is a library for creating structured and styled HTML emails, intended as a transitional resource. Designed for convenience, it allows developers to build and customize emails quickly while encouraging best practices by illustrating the internal composition of emails through self-contained components. Inspired by Resend's @react-email.
This library takes a similar approach to the one applied by pilcrow's to The Copenhagen Book: this library is intended as a learning tool and a starting point. As developers become comfortable with the email structure and utilities provided, they are encouraged to transition toward standalone solutions.
To install the package, run:
npm install @uraniadev/emailer
This library provides a set of unstyled, modular email components (e.g.,
Button
, Container
, Heading
) built with Svelte and styled with Tailwind
CSS. Components use inline
styling through tw-to-css
to ensure email
compatibility.
Create an email using the provided components:
<!-- src/lib/email.svelte -->
<script>
import {
Button,
Container,
Heading,
Image,
Paragraph
} from "@uraniadev/emailer";
</script>
<Container>
<Image src="https://example.org/image-url.jpg" />
<Heading level={2}>Lorem ipsum dolor sit</Heading>
<Paragraph>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Maiores veritatis earum, perspiciatis pariatur exercitationem
illum eligendi amet,deserunt provident ipsum dolore!
Voluptas mollitia earum temporibus, hic consequatur
aperiam recusandae.
</Paragraph>
<Button href="https://www.example.com">Deserunt</Button>
</Container>
Then render this email in your server-side script:
import Emailer from "@uraniadev/emailer";
import Email from "./email.svelte";
const emailer = new Emailer();
const html = emailer.render(Email, {/* props */});
sendMail(html); // Use your preferred email-sending method
tw-to-css
This library includes an inline
utility that merges Tailwind styles with
tw-to-css
to create compatible inline styles for emails:
import { inline } from "@uraniadev/emailer";
<p style={inline("text-lg text-pink-500 font-bold")}>
This is a formatted text
</p>;
// inline() inherith shadcn cn an apply tw-to-css inline:
import type { ClassValue } from "clsx";
import clsx from "clsx";
import { twMerge } from "tailwind-merge";
import { twi } from "tw-to-css";
export function inline(...inputs: ClassValue[]) {
return twi(twMerge(clsx(inputs)));
}
Button
A link-styled button that accepts class
and href
attributes. *
<Button class="bg-blue-500 text-white" href="https://example.com">Click Me</Button>
Card
A general-purpose card component for grouping content sections.
<Card class="shadow-lg">This is a card</Card>
Container
A wrapper to align email content and provide consistent padding.
<Container class="max-w-lg">Content goes here</Container>
Heading
A responsive heading element allowing level-based customization.
<Heading level={2}>This is a Heading</Heading>
Image
An image wrapper with optional classes.
<Image src="https://example.com/image.jpg" alt="Image description" />
Paragraph
For general text blocks, styled for readability.
<Paragraph>This is a paragraph of text</Paragraph>
Preview
This is a text snippet that appears under the subject line in the inbox but is hidden from the main email content when it’s opened. *
<Preview content="The preview of the content, to customize it from the actual mail" />
Repeatable
Generates repeated content, such as lists, from an array. itemsSnippet
can be
use to stylize the repeated item
<Repeatable items={[1, 2, 3]}>
{#snippet itemsSnippet(item)}
<Container>
<Paragraph>{item.name}</Paragraph>
</Container>
{/snippet}
</Repeatable>
Emailer
is the primary class used to render emails, taking in a Svelte
component and outputting an HTML template. It can be customized by passing
configuration options for props
, lang
, dir
, and style
.
class Emailer {
render<T extends Component<any>>(
component: T,
props?: ComponentProps<T>,
config?: HTMLConfig,
): string;
}
The htmlBoilerplate
function in the Emailer
class generates the complete
HTML structure for email content, applying essential styles and configuration
for better rendering across email clients. This function wraps the main content
(children
) in an HTML template, ensuring consistency and compatibility.
html
, body
, and
container
elements, configurable via the HTMLConfig
type. Each property is
an array of style strings, providing styles like background color, text color,
and padding.<style>
tag.render
functionThe Emailer.render()
method uses htmlBoilerplate
to wrap the rendered body
of a Svelte component (provided in render
as component
). The function also
accepts:
<head>
section.HTMLConfig
object to override the default
properties, styles, direction, or language settings.Together, these properties and htmlBoilerplate
facilitate the creation of
highly customizable, visually consistent email templates with minimal setup.
htmlBoilerplate = (children: string, head?: string, options?: HTMLConfig) => {
const { props, dir, lang, style } = options ||
{ props: this.props, dir: this.dir, lang: this.lang, style: this.style };
return `<!doctype html>
<html ${props?.html.join(" ")} dir=${dir} lang=${lang}>
<head>
<style>*{box-sizing:border-box;text-decoration:none;border:0;padding:0;margin:0;}${style}</style>
${head || this.head}
</head>
<body ${props?.body.join(" ")}>
<table ${props?.container.join(" ")}>
<tbody><tr><td>${children}</td></tr></tbody>
</table>
<body>
</html>`;
};
The library is structured as an auto-deprecating toolkit for email composition, encouraging developers to understand and apply best practices for maintainable email templates in production. The following approaches are suggested:
Drawing inspiration from resources like The Copenhagen Book, this library advocates for transparency in development practices and empowers developers to confidently transition to self-maintained code structures.
The following references provide further information on creating HTML emails:
Note: This library is intended as a learning tool and a starting point. As developers become comfortable with the email structure and utilities provided, they are encouraged to transition toward standalone solutions.