Overview
The sendEmail() function returns a SvelteKit form action that sends emails using Resend or a custom email provider. Use this with the EmailPreview component to test and send emails directly from your browser.
Function Signature
function sendEmail(options?: SendEmailOptions): {
'send-email': (event: RequestEvent) => Promise<SendEmailResponse>
}
Parameters
options
SendEmailOptions
default:"{}"
Optional configuration objectYour Resend API key. Required if not using a custom send function
options.customSendEmailFunction
Custom function to send emails using your preferred providerFunction Signature:(email: {
from: string;
to: string;
subject: string;
html: string;
}) => Promise<{
success: boolean;
error?: any;
}>
Custom renderer instance with your Tailwind config. If not provided, uses default renderer
Default sender email address
Return Type
SvelteKit actions object to be exported from +page.server.tsForm action that handles email sending requestsSuccess Response:true if email was sent successfully
Error Response:false if email failed to send
Error details from the email provider
Usage
Basic Example with Resend
import { sendEmail } from 'better-svelte-email/preview';
import { PRIVATE_RESEND_API_KEY } from '$env/static/private';
export const actions = sendEmail({
resendApiKey: PRIVATE_RESEND_API_KEY
});
Security: Never expose your Resend API key to the client. Always import it from $env/static/private or use environment variables on the server.
Custom Sender Address
import { sendEmail } from 'better-svelte-email/preview';
import { PRIVATE_RESEND_API_KEY } from '$env/static/private';
export const actions = sendEmail({
resendApiKey: PRIVATE_RESEND_API_KEY,
from: 'Your App <[email protected]>'
});
With Custom Renderer
import { sendEmail } from 'better-svelte-email/preview';
import { PRIVATE_RESEND_API_KEY } from '$env/static/private';
import Renderer from 'better-svelte-email/render';
const renderer = new Renderer({
tailwindConfig: {
theme: {
extend: {
colors: {
brand: '#FF3E00'
}
}
}
}
});
export const actions = sendEmail({
resendApiKey: PRIVATE_RESEND_API_KEY,
renderer
});
Complete Setup
import { emailList, createEmail, sendEmail } from 'better-svelte-email/preview';
import { PRIVATE_RESEND_API_KEY } from '$env/static/private';
import Renderer from 'better-svelte-email/render';
const renderer = new Renderer({
tailwindConfig: {
theme: {
extend: {
colors: {
brand: '#FF3E00'
}
}
}
}
});
export function load() {
const emails = emailList();
return { emails };
}
export const actions = {
...createEmail({ renderer }),
...sendEmail({
resendApiKey: PRIVATE_RESEND_API_KEY,
renderer,
from: 'Your App <[email protected]>'
})
};
<script>
import { EmailPreview } from 'better-svelte-email/preview';
export let data;
</script>
<EmailPreview {data} />
Custom Email Provider
You can use any email provider by providing a custom send function:
Postmark Example
import { sendEmail } from 'better-svelte-email/preview';
import { ServerClient } from 'postmark';
import { POSTMARK_API_KEY } from '$env/static/private';
const client = new ServerClient(POSTMARK_API_KEY);
const customSendFunction = async (email: {
from: string;
to: string;
subject: string;
html: string;
}) => {
try {
await client.sendEmail({
From: email.from,
To: email.to,
Subject: email.subject,
HtmlBody: email.html
});
return { success: true };
} catch (error) {
return { success: false, error };
}
};
export const actions = sendEmail({
customSendEmailFunction: customSendFunction,
from: 'Your App <[email protected]>'
});
SendGrid Example
import { sendEmail } from 'better-svelte-email/preview';
import sgMail from '@sendgrid/mail';
import { SENDGRID_API_KEY } from '$env/static/private';
sgMail.setApiKey(SENDGRID_API_KEY);
const customSendFunction = async (email: {
from: string;
to: string;
subject: string;
html: string;
}) => {
try {
await sgMail.send({
from: email.from,
to: email.to,
subject: email.subject,
html: email.html
});
return { success: true };
} catch (error) {
return { success: false, error };
}
};
export const actions = sendEmail({
customSendEmailFunction: customSendFunction,
from: 'Your App <[email protected]>'
});
When using a custom send function, you don’t need to provide resendApiKey. The custom function takes precedence.
How It Works
-
The
EmailPreview component submits a form with email details:
file: Email component filename
path: Path to emails directory
to: Recipient email address
component: Component name (used in subject)
note: Optional note (appended to subject)
-
The
send-email action:
- Dynamically imports the email component
- Renders it to HTML using the provided renderer
- Constructs the email object
- Sends via Resend or custom function
-
The response indicates success or failure
The subject line is automatically generated:
Examples:
- Component:
welcome, Note: empty → Subject: welcome
- Component:
welcome, Note: Testing new design → Subject: welcome | Testing new design
Error Handling
Missing Parameters
{
"success": false,
"error": {
"message": "Missing file or path parameter"
}
}
Missing API Key
{
"success": false,
"error": {
"message": "Resend API key not configured. Please pass your API key to the sendEmail() function in your +page.server.ts file."
}
}
Provider Error
Errors from Resend or your custom provider are passed through:
{
"success": false,
"error": {
"message": "Invalid recipient email address",
"name": "validation_error"
}
}
Test emails in development before sending in production. Email deliverability can be affected by:
- Invalid sender/recipient addresses
- Domain verification status
- Provider rate limits
- Spam filters
Best Practices
Environment Variables
Always use environment variables for API keys:
PRIVATE_RESEND_API_KEY=re_xxxxxxxxxxxx
Sender Domain
For production, use a verified domain:
export const actions = sendEmail({
resendApiKey: PRIVATE_RESEND_API_KEY,
from: 'Your App <[email protected]>' // Verified domain
});
Rate Limiting
Implement rate limiting to prevent abuse:
import { sendEmail } from 'better-svelte-email/preview';
import { PRIVATE_RESEND_API_KEY } from '$env/static/private';
import { rateLimit } from '$lib/rate-limit';
export const actions = {
'send-email': async (event) => {
// Check rate limit
const limited = await rateLimit(event.getClientAddress());
if (limited) {
return {
success: false,
error: { message: 'Too many requests. Please try again later.' }
};
}
// Send email
const sendAction = sendEmail({ resendApiKey: PRIVATE_RESEND_API_KEY });
return sendAction['send-email'](event);
}
};
Access Control
Restrict preview interface to authorized users:
import { redirect } from '@sveltejs/kit';
export async function load({ locals }) {
// Only allow admins to access email preview
if (!locals.user?.isAdmin) {
throw redirect(302, '/login');
}
const emails = emailList();
return { emails };
}
- emailList() - Get list of available email components
- createEmail() - Render email components on demand
- Renderer - Learn more about custom Tailwind configuration