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-cssThis 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)));
}
ButtonA link-styled button that accepts class and href attributes. *
<Button class="bg-blue-500 text-white" href="https://example.com">Click Me</Button>
CardA general-purpose card component for grouping content sections.
<Card class="shadow-lg">This is a card</Card>
ContainerA wrapper to align email content and provide consistent padding.
<Container class="max-w-lg">Content goes here</Container>
HeadingA responsive heading element allowing level-based customization.
<Heading level={2}>This is a Heading</Heading>
ImageAn image wrapper with optional classes.
<Image src="https://example.com/image.jpg" alt="Image description" />
ParagraphFor general text blocks, styled for readability.
<Paragraph>This is a paragraph of text</Paragraph>
PreviewThis 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" />
RepeatableGenerates 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.