Rendering emails with Svelte

This page summarizes the projects mentioned and recommended in the original post on dev.to

Our great sponsors
  • SurveyJS - Open-Source JSON Form Builder to Create Dynamic Forms Right in Your App
  • InfluxDB - Power Real-Time Data Analytics at Scale
  • WorkOS - The modern identity platform for B2B SaaS
  • Svelte

    Cybernetically enhanced web apps

  • If you started web development before 2000, chances are you worked on websites designed with -based layouts. It was the way to design complex two-dimensional layouts.Well, unfortunately for us, email clients are still stuck in the dark ages. We, therefore, have four possibilities to write emails: Write them by hand and learn the quirks of the old -based layout system with loads of ${body} `; const html = mjml2html(document(mail({ name: "World" }))); Enter fullscreen mode Exit fullscreen mode 4. We would send the resulting HTML: let transporter = nodemailer.createTransport(); await transporter.sendMail({ from: "[email protected]", to: "[email protected]", subject: "Hello!", html, }); Enter fullscreen mode Exit fullscreen mode Apart from that, we also want: A way to preview the emails, and the easiest way to get live-reload in a Svelte project is to use SvelteKit. Compile-time type checking for props, which is made possible by svelte2tsx. Our setup will be in two parts: Setting up a development server to create and preview emails. Setting up a build pipeline to compile emails to HTML strings. The dev server SvelteKit offers a fantastic developer experience to work with Svelte and was just released as stable, so it is a no-brainer to use it. You can clone the whole experiment from GitHub. We will go through the most interesting parts in the rest of the article. You will find a complete SvelteKit project in packages/svelte-emails: index.ts: This is our library entry point. // Export the renderer export { render } from "./lib/index.js"; // Also export compiled Svelte components export * from "./mails/index.js"; Enter fullscreen mode Exit fullscreen mode lib/ Header.svelte: This is our common email header. MJML offers a lot of components out of the box. align="center" font-size="20px" font-family="Helvetica"> /> border-color="#ff3e00" /> index.ts: It contains the MJML rendering logic. /** Renders a Svelte component as email-ready HTML. */ export const render = ( component: new (...args) => SvelteComponentTyped, props: Props ) => { // Render the component to MJML const { html: body, css, head } = component.render(props); const mjml = ` ${head} ${css.code} ${body} `; // Render MJML to HTML const { html } = mjml2html(mjml); return html; }; mails/: This is the root HTTP directory, and it will also contain our emails. index.ts: This file reexports all the emails. export { default as HelloWorld } from "./hello-world/Mail.svelte"; hello-world/ Mail.svelte: Make a guess! Hello {name}! color="#fff" background-color="#ff3e00" font-family="Helvetica" href="https://svelte.dev" > Learn Svelte +page.server.ts and +page.svelte: These are our development email previews, powered by Vite. export const load = async () => ({ email: render(Mail, { // This is type-checked! name: "World", }), }); That is quite a lot of code! Let's try it out: # Start the dev server yarn dev Enter fullscreen mode Exit fullscreen mode Go to localhost:5173/hello-world to see the email preview, and edit anything to see it update in real-time. The build pipeline We now have a working development environment, but we need to build our emails for production. We will use Rollup to bundle our emails, and svelte2tsx to emit type declarations. The rollup.config.js file defines our build pipeline: export default { input: "src/mails/index.ts", plugins: [ { /** Export component's types at the end of the build. */ name: "rollup-plugin-svelte2dts", async buildEnd() { const require = createRequire(import.meta.url); // All the heavy lifting is done by svelte2tsx await emitDts({ svelteShimsPath: require.resolve("svelte2tsx/svelte-shims.d.ts"), declarationDir: "build", }); // We need to replace `.svelte` with `.svelte.js` for types to be resolved const index = "build/mails/index.d.ts"; const code = await readFile(index, "utf-8"); await writeFile(index, code.replaceAll(".svelte", ".svelte.js")); }, }, svelte({ ...svelteConfig, compilerOptions: { generate: "ssr" }, emitCss: false, }), ], }; Enter fullscreen mode Exit fullscreen mode Run yarn build to transform the Svelte emails components into raw JavaScript. Wrapping up Using our built emails in a NodeJS app is as simple as: import { render, HelloWorld } from "svelte-emails"; const html = render(HelloWorld, { // This is type-checked! name: "World", }); console.log(html); Enter fullscreen mode Exit fullscreen mode There is a demo package in the repo for you to try out. This concludes this experiment. We are still tinkering with a few things to make it easier to use, but we hope you enjoyed this article. Feel free to ask questions or give feedback on how you are currently developing emails, we are eager to hear from you!

  • SvelteKit

    web development, streamlined (by sveltejs)

  • We, at Escape, recently rebuilt our whole email stack from scratch to improve the developer experience: we used to send emails to preview them, whereas now, we have an instant feedback loop, leveraging a SvelteKit-powered dev server.

  • SurveyJS

    Open-Source JSON Form Builder to Create Dynamic Forms Right in Your App. With SurveyJS form UI libraries, you can build and style forms in a fully-integrated drag & drop form builder, render them in your JS app, and store form submission data in any backend, inc. PHP, ASP.NET Core, and Node.js.

    SurveyJS logo
NOTE: The number of mentions on this list indicates mentions on common posts plus user suggested alternatives. Hence, a higher number means a more popular project.

Suggest a related project

Related posts