The Email Preview component provides a visual development environment for creating and testing your email templates. It includes features like live preview, HTML source and code source viewing, and test email sending—all within your SvelteKit application.
Use better-svelte-email/preview. For v2, see Email dev server (beta) (@better-svelte-email/cli).
Try it live! Check out the preview in action in this page. You can explore sample email templates and see how the preview component works before setting it up in your own project.
Create a new route under src/routes/email-preview/[...email] in your SvelteKit app to host the preview component:
<!-- src/routes/email-preview/[...email]/+page.svelte -->
<script lang="ts">
import { EmailPreview } from 'better-svelte-email/preview';
import { page } from '$app/state';
</script>
<EmailPreview {page} /> Create a +page.server.ts file in the same route to load your email templates and handle preview actions:
// src/routes/email-preview/[...email]/+page.server.ts
import { emailList, createEmail, sendEmail } from 'better-svelte-email/preview';
import { env } from '$env/dynamic/private';
export function load() {
const emails = emailList({
path: '/src/lib/emails' // optional, defaults to '/src/lib/emails'
});
return { emails };
}
export const actions = {
...createEmail(),
...sendEmail({ resendApiKey: env.RESEND_API_KEY, from: 'onboarding@resend.dev' })
}; Note: If you want to use a different email provider (SendGrid, Mailgun, etc.) instead of Resend, see the Using a Custom Email Provider section below.
To enable test email sending, add your Resend API key to your .env file:
RESEND_API_KEY=re_your_api_key_here Get your API key from Resend.
To use Tailwind in your email templates, you need to pass a renderer instance to the createEmail and sendEmail actions.
import Renderer from 'better-svelte-email/render';
import { createEmail, sendEmail } from 'better-svelte-email/preview';
const tailwindConfig = {
theme: { extend: { colors: { brand: '#FF3E00' } } }
};
const renderer = new Renderer({ tailwindConfig });
export const actions = {
...createEmail({ renderer }),
...sendEmail({ renderer })
}; You can also pass your app’s CSS (including CSS variables) to the Renderer class.
import layoutStyles from 'src/routes/layout.css?raw';
const renderer = new Renderer({ customCSS: layoutStyles }); By default, the preview looks for email templates in /src/lib/emails. You can customize this path:
export function load() {
const emails = emailList({
path: '/src/lib/custom-emails'
});
return { emails };
} If your project structure requires it, you can specify a custom root path:
export function load() {
const emails = emailList({
root: process.cwd(),
path: '/src/lib/emails'
});
return { emails };
} If you prefer to use a different email service (SendGrid, Mailgun, etc.), pass a custom send function:
export const actions = {
...createEmail(),
...sendEmail({
customSendEmailFunction: async ({ from, to, subject, html }) => {
// Use your preferred email service
try {
await yourEmailService.send({ from, to, subject, html });
return { success: true };
} catch (error) {
return { success: false, error };
}
}
})
}; Your custom send function should accept an object with these properties:
from (string) - Sender email addressto (string) - Recipient email addresssubject (string) - Email subject linehtml (string) - Rendered HTML contentAnd return an object with:
success (boolean) - Whether the email was sent successfullyerror (any, optional) - Error details if sending failedimport { emailList, createEmail, sendEmail } from 'better-svelte-email/preview';
import sgMail from '@sendgrid/mail';
import { env } from '$env/dynamic/private';
sgMail.setApiKey(env.SENDGRID_API_KEY);
export function load() {
return { emails: emailList() };
}
export const actions = {
...createEmail(),
...sendEmail({
customSendEmailFunction: async ({ from, to, subject, html }) => {
try {
await sgMail.send({ from, to, subject, html });
return { success: true };
} catch (error) {
console.error('SendGrid error:', error);
return { success: false, error };
}
}
})
}; If you’re using Vercel serverless functions, the createEmail and sendEmail actions will not work out of the box. You will need to create some custom serverless functions to handle the actions.
You can see an example implementation in this file.
Make sure:
/src/lib/emails).svelte extensionemailList() matches your actual directory structureVerify that:
RESEND_API_KEY is set in your .env filesendEmail action in your +page.server.tsEnsure:
+page.svelte and +page.server.ts in your preview routehttp://localhost:5173/email-preview)emailList(options)Loads all email templates from the specified directory.
Parameters:
options.path (string, optional) - Relative path from root to emails folder (default: /src/lib/emails)options.root (string, optional) - Absolute path to project root (auto-detected if not provided)Returns: PreviewData object with:
files (string[] | null) - Array of email template file namespath (string | null) - Path to the emails directorycreateEmailSvelteKit form action that renders an email component to HTML.
sendEmail(options)Returns a SvelteKit form action that sends test emails.
Parameters:
options.resendApiKey (string, optional) - Your Resend API keyoptions.customSendEmailFunction (function, optional) - Custom email sending functionoptions.from (string, optional) - Sender email address (defaults to ‘better-svelte-email onboarding@resend.dev‘)