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 features
- fix:- Bug fixes
- docs:- Documentation changes
- test:- Test additions/changes
- chore:- 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.