Solid
Maizzle plugs into SolidStart as a Vite plugin. Run npm start and a sibling preview server starts alongside your app; run npm run build and email templates compile to static HTML.
Installation
In this guide, we'll use SolidStart as the framework.
After setting up SolidStart, install Maizzle:
npm install @maizzle/framework
Setup
Project structure
Create an emails directory inside src for your email templates:
├── src/
│ ├── emails/
│ │ ├── welcome.vue
│ │ └── tsconfig.json
│ ├── routes/
├── app.config.ts
├── tsconfig.json
└── package.json
App config
Register the Maizzle Vite plugin in the vite.plugins option in your app.config.ts:
import { defineConfig } from '@solidjs/start/config'
import { maizzle } from '@maizzle/framework'
export default defineConfig({
vite: {
plugins: [
maizzle({
root: 'src/emails',
content: ['**/*.vue'],
output: {
path: 'build/emails',
},
}),
],
},
})
See Configuration for all available options.
TypeScript
Maizzle generates type declarations for auto-imported components and composables post-installation. To enable type checking for your email templates, add a tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"composite": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": [
"./**/*.vue",
"./.maizzle/*.d.ts"
]
}
Then add it as a project reference in your root tsconfig.json:
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js",
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"noEmit": true,
"strict": true
},
"references": [
{ "path": "./src/emails" } ]
}
Usage
Create Vue SFC email templates in your src/emails directory. Maizzle components like Layout, Container, Button, etc. are auto-imported:
<script setup>
const name = 'World'
</script>
<template>
<Layout>
<Container class="max-w-xl">
<Heading class="text-2xl">Hello, {{ name }}!</Heading>
<Button
href="https://example.com"
class="bg-slate-950 hover:bg-slate-800"
>Get Started</Button>
</Container>
</Layout>
</template>
Development
Run your dev command as usual. Maizzle starts its own dev server alongside SolidStart:
npm run dev
- Your SolidStart app runs on its default port (typically
3000) - The Maizzle email preview UI runs on the next available port (normally
3001)
Changes to email templates are automatically reflected in the Maizzle preview UI.
Production build
When you build your app, Maizzle compiles your email templates to static HTML files in the configured output.path, which in our example is build/emails:
npm run build
Server API
You can render email templates on-demand using Maizzle's render function in a SolidStart API route. This is useful when you need to render emails dynamically, for example with user data from a database.
The render function accepts either a file path or an SFC string directly, and returns compiled HTML with CSS inlined, purged, and formatted.
API route
Create a server endpoint that renders an email template. You can read a .vue file from disk:
import { resolve } from 'node:path'
import { render } from '@maizzle/framework'
export async function GET() {
const { html } = await render(resolve('src/emails/welcome.vue'))
return Response.json({ html })
}
Or accept an SFC string in the request body:
import { resolve } from 'node:path'
import { render } from '@maizzle/framework'
import type { APIEvent } from '@solidjs/start/server'
export async function POST({ request }: APIEvent) {
const { template } = await request.json()
const { html } = await render(template)
return Response.json({ html })
}
Displaying the result
Since the rendered email is a full HTML document, use an iframe to display it in a SolidStart page:
import { onMount } from 'solid-js'
export default function Preview() {
let iframeRef: HTMLIFrameElement | undefined
onMount(async () => {
const res = await fetch('/api/render')
const { html } = await res.json()
if (iframeRef) {
iframeRef.srcdoc = html
}
})
return <iframe ref={iframeRef} style={{ width: '100%', height: '100vh', border: 'none' }} />
}
Sending emails
You can use the rendered HTML to send emails. Here's an example using Nodemailer:
import type { APIEvent } from '@solidjs/start/server'
import { resolve } from 'node:path'
import { render } from '@maizzle/framework'
import { createTransport } from 'nodemailer'
const transporter = createTransport({
host: 'smtp.example.com',
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
})
export async function POST({ request }: APIEvent) {
const { to } = await request.json()
const { html } = await render(resolve('src/emails/welcome.vue'))
await transporter.sendMail({
from: '[email protected]',
to,
subject: 'Welcome!',
html,
})
return Response.json({ success: true })
}
Static assets
To include images or other static files with your emails, configure the static option on the Maizzle plugin in app.config.ts:
import { defineConfig } from '@solidjs/start/config'
import { maizzle } from '@maizzle/framework'
export default defineConfig({
vite: {
plugins: [
maizzle({
content: ['./src/emails/**/*.vue'],
output: {
path: 'build/emails',
},
static: { source: ['src/emails/images'], }, }),
],
},
})
Static files are copied to the output directory during production builds.
Other frameworks
Not using Solid? Check out the other framework guides: