Create beautiful emails in Svelte with first-class Tailwind support
- Stable & Future-Proof - Uses Svelte's public preprocessor API
- Tailwind CSS Support - Transforms Tailwind classes to inline styles for email clients
- Built-in Email Preview - Visual email preview and test sending
- TypeScript First - Fully typed with comprehensive type definitions
- Well Tested - Extensive test coverage with unit and integration tests
See Roadmap for future features and planned improvements.
Existing Svelte email solutions have significant limitations:
- svelte-email hasn't been updated in over 2 years
- svelte-email-tailwind suffers from stability issues and maintaining it is not sustainable anymore
Better Svelte Email is a complete rewrite built on Svelte's official preprocessor API, providing the rock-solid foundation your email infrastructure needs. It brings the simplicity, reliability, and feature richness of React Email to the Svelte ecosystem.
npm i -D better-svelte-email
# or
bun add -D better-svelte-email
# or
pnpm add -D better-svelte-emailAdd the preprocessor to your svelte.config.js:
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import { betterSvelteEmailPreprocessor } from 'better-svelte-email';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: [vitePreprocess(), betterSvelteEmailPreprocessor()],
kit: {
adapter: adapter()
}
};
export default config;Create your email templates in src/lib/emails/:
<!-- src/lib/emails/welcome.svelte -->
<script>
import { Html, Head, Body, Preview, Container, Text, Button } from 'better-svelte-email';
let { name = 'User' } = $props();
</script>
<Html>
<Head />
<Body class="bg-gray-100">
<Preview preview="Welcome Email" />
<Container class="mx-auto p-8">
<Text class="mb-4 text-2xl font-bold">
Welcome, {name}!
</Text>
<Button
href="https://example.com"
class="rounded bg-orange-600 px-6 py-3 text-white sm:text-sm"
>
Get Started
</Button>
</Container>
</Body>
</Html>// src/routes/api/send-email/+server.ts
import { render } from 'svelte/server';
import WelcomeEmail from '$lib/emails/welcome.svelte';
export async function POST({ request }) {
const { name, email } = await request.json();
// Render email (preprocessor already ran at build time!)
const result = render(WelcomeEmail, { props: { name } });
// Send email using your preferred service (Resend, SendGrid, etc.)
// await resend.emails.send({
// from: 'noreply@example.com',
// to: email,
// subject: 'Welcome!',
// html: result.body
// });
return new Response('Sent');
}Better Svelte Email includes a built-in preview component for visually developing and testing your email templates during development.
Create a preview route in your SvelteKit app:
<!-- src/routes/preview/+page.svelte -->
<script lang="ts">
import { EmailPreview } from 'better-svelte-email/preview';
let { data } = $props();
</script>
<EmailPreview emailList={data.emails} />// src/routes/preview/+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 })
};- HTML Source View - Inspect the generated HTML with syntax highlighting
- Copy to Clipboard - Quickly copy the rendered HTML
- Test Email Sending - Send test emails directly from the preview UI using Resend
- Template List - Browse all your email templates in one place
To enable test email sending, add your Resend API key to your .env file:
RESEND_API_KEY=re_your_api_key_hereGet your API key from Resend.
If you prefer to use a different email provider, you can pass a custom send function:
export const actions = {
...createEmail,
...sendEmail({
customSendEmailFunction: async ({ from, to, subject, html }) => {
// Use your preferred email service (SendGrid, Mailgun, etc.)
try {
await yourEmailService.send({ from, to, subject, html });
return { success: true };
} catch (error) {
return { success: false, error };
}
}
})
};Here are the available options:
betterSvelteEmailPreprocessor({
pathToEmailFolder: '/src/lib/emails',
debug: false,
tailwindConfig: {
theme: {
extend: {
colors: {
brand: '#FF3E00'
}
}
}
}
});The minimum supported Svelte version is 5.14.3.
For older versions, you can use svelte-email-tailwind.
- β Static Tailwind classes
- β
Custom Tailwind classes (
bg-[#fff],my:[40px], ...) - β All standard Tailwind (v3) utilities (colors, spacing, typography, etc.)
- β
Responsive breakpoints (
sm:,md:,lg:,xl:,2xl:) - β HTML elements and Svelte components
- β Nested components
- β
Conditional blocks (
{#if}) - β
Each blocks (
{#each}) - β Custom Tailwind configurations
β Not Supported (Yet) (See Roadmap)
- β Tailwind v4
- β CSS Object (
style={{ color: 'red' }}) - β Dynamic class expressions (
class={someVar}) - β Arbitrary values in responsive classes (
sm:[color:red]) - β Container queries
Anatole Dufour (@Konixy)
bun run testAll tests must pass before pushing to main. The CI/CD pipeline will automatically run tests on every push and pull request.
bun run buildContributions are welcome! Please feel free to submit a Pull Request.
To do so, you'll need to:
- Fork the repository
- Create a feature branch (
git checkout -b feat/amazing-feature) - Make your changes
- Run tests (
bun run test) - Commit your changes using conventional commits:
feat:- New featuresfix:- Bug fixesdocs:- Documentation changestest:- Test additions/changeschore:- Maintenance tasks
- Push to your branch (
git push origin feat/amazing-feature) - Open a Pull Request
Many components and logic were inspired by or adapted from svelte-email-tailwind and react-email. Huge thanks to the authors and contributors of these projects for their excellent work.