SVELTE 5 COMPATIBLE since version 2.0.0!
svelte-email-tailwind
enables you to code, preview and test-send email templates with Svelte and Tailwind classes and render them to HTML or plain text.
Install the package to your existing Svelte + Vite or SvelteKit project:
npm install svelte-email-tailwind
pnpm install svelte-email-tailwind
Import the svelteEmailTailwind Vite plugin, and pass it into the config's plugins
array.
vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import type { UserConfig } from 'vite';
import svelteEmailTailwind from 'svelte-email-tailwind/vite';
const config: UserConfig = {
plugins: [
sveltekit(),
svelteEmailTailwind() // processes .svelte files inside the default '/src/lib/emails' folder
]
};
export default config;
Optional configurations:
import { sveltekit } from '@sveltejs/kit/vite';
import type { UserConfig } from 'vite';
import type { TailwindConfig } from 'tw-to-css';
import svelteEmailTailwind from 'svelte-email-tailwind/vite';
const emailTwConfig: TailwindConfig = {
theme: {
screens: {
md: { max: '767px' },
sm: { max: '475px' }
},
extend: {
colors: {
brand: 'rgb(255, 62, 0)'
}
}
}
};
const config: UserConfig = {
plugins: [
sveltekit(),
svelteEmailTailwind({
tailwindConfig: emailTwConfig,
pathToEmailFolder: '/src/lib/components/emails' // defaults to '/src/lib/emails'
})
]
};
export default config;
src/lib/emails/Hello.svelte
<script>
import { Button, Hr, Html, Text, Head } from 'svelte-email-tailwind';
export let name = 'World';
</script>
<Html lang="en">
<Head />
<Text class="md:text-[18px] text-[24px]">
Hello, {name}!
</Text>
<Hr />
<Button href="https://svelte.dev">Visit Svelte</Button>
</Html>
This example uses Resend to send the email. You can use any other email service provider (Nodemailer, SendGrid, Postmark, AWS SES...).
src/routes/emails/hello/+server.ts
import { render } from 'svelte/server';
import type { ComponentProps } from 'svelte';
import type HelloProps from 'src/lib/emails/Hello.svelte';
import Hello from 'src/lib/emails/Hello.svelte';
import { PRIVATE_RESEND_API_KEY } from '$env/static/private';
import { Resend } from 'resend';
const componentProps: ComponentProps<HelloProps> = {
name: 'Steven'
};
const { html } = render(Hello, { props: componentProps });
const resend = new Resend(PRIVATE_RESEND_API_KEY);
// Send the email using your provider of choice.
resend.emails.send({
from: '[email protected]',
to: '[email protected]',
subject: 'Hello',
html: html
});
Using a designated route, you can preview all your dynamically retrieved email components. This means you'll be able to preview your emails with the exact markup that eventually lands an inbox (unless of course, the email provider manipulates it behind the scenes).
To get started...
Import the Preview component and pass in the server data as a prop. Customize the email address.
src/routes/email-previews/+page.svelte
<script lang="ts">
import Preview from 'svelte-email-tailwind/preview/preview.svelte';
export let data;
</script>
<Preview {data} email="[email protected]" />
Return the email component file list from SvelteKit's load
function using the emailList
function.
In SvelteKit's form actions
, pass in createEmail
(loads files from the server), and sendEmail
(sends test-emails).
src/routes/email-previews/+page.server.ts
import { createEmail, emailList, sendEmail } from 'svelte-email-tailwind/preview';
import { PRIVATE_RESEND_API_KEY } from '$env/static/private';
export async function load() {
// return the list of email components
return emailList();
}
export const actions = {
// Pass in the two actions. Provide your Resend API key.
...createEmail,
...sendEmail({ resendApiKey: PRIVATE_RESEND_API_KEY })
};
Optional configurations:
import {
createEmail,
emailList,
sendEmail,
SendEmailFunction
} from 'svelte-email-tailwind/preview';
import nodemailer from 'nodemailer';
export async function load() {
// Customize the path to your email components.
return emailList({ path: '/src/lib/components/emails' });
}
// Make sure your custom 'send email' function is of type 'SendEmailFunction'.
const sendUsingNodemailer: typeof SendEmailFunction = async ({ from, to, subject, html }) => {
const transporter = nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
secure: false,
auth: {
user: 'my_user',
pass: 'my_password'
}
});
const sent = await transporter.sendMail({ from, to, subject, html });
if (sent.error) {
return { success: false, error: sent.error };
} else {
return { success: true };
}
};
export const actions = {
...createEmail,
// Pass in your custom 'send email' function.
...sendEmail({ customSendEmailFunction: sendUsingNodemailer })
};
Example: http://localhost:5173/email-previews
A set of standard components to help you build amazing emails without having to deal with the mess of creating table-based layouts and maintaining archaic markup.
<Head />
component.class={someTwClassName}
, class={
${someTwClassName} w-full}
, this will work: class="w-full"
).<Custom />
component and the "as" property to define the tag. This component defaults to a <div/>
. Tailwind classes on regular html nodes will not be processed.node_invalid_placement_ssr:
` (src/lib/components/Html.svelte:12:0) needs a valid parent element
This can cause content to shift around as the browser repairs the HTML, and will likely result in a hydration_mismatch
warning.`
You can ignore these warnings, because Svelte thinks you're building for the web and doesn't know you're building emails - so the warnings are not applicable.