Skip to content
Back to skills

Stacks Mail

ASecurity

Use when creating mail classes in app/Mail/ - defining email content and templates, using the template() function with STX or HTML templates, variable interpolation, email layouts, or the app-level mail sending pattern. For the email framework itself (drivers, Mail singleton, EmailSDK, inbox management), see stacks-email.

  • 631 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added June 5, 2026
developmenttypescriptgobashreact

Works with

  • cli

Security analysis

A100/100

Scanned October 1, 2026

npx -y skills add stacksjs/stacks --skill stacks-mail --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Stacks Mail?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Stacks Mail
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/stacksjs-stacks-mail/badge)](https://www.skillsdirectory.com/skills/stacksjs-stacks-mail)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: stacks-mail
description: Use when creating mail classes in app/Mail/ - defining email content and templates, using the template() function with STX or HTML templates, variable interpolation, email layouts, or the app-level mail sending pattern. For the email framework itself (drivers, Mail singleton, EmailSDK, inbox management), see stacks-email.
license: MIT
compatibility: Bun >= 1.3.0, TypeScript
allowed-tools: Read Edit Write Bash Grep Glob
---

# Stacks Mail Classes

Application-level email definitions in `app/Mail/`.

## Key Paths
- Application mail: `app/Mail/`
- Email templates: `resources/emails/` (or `storage/framework/defaults/resources/emails/`)
- Email layouts: `storage/framework/defaults/resources/emails/layouts/`

## Creating a Mail Class

```typescript
// app/Mail/WelcomeEmail.ts
import { mail, template } from '@stacksjs/email'
import { config } from '@stacksjs/config'

interface WelcomeEmailOptions {
  to: string
  name: string
}

export async function sendWelcomeEmail({ to, name }: WelcomeEmailOptions) {
  const { html, text } = await template('welcome', {
    variables: { name, appName: config.app.name },
    layout: 'default'
  })

  await mail.send({
    from: { name: config.app.name, address: config.email.from.address },
    to,
    subject: `Welcome to ${config.app.name}!`,
    html,
    text
  })
}
```

## Template Rendering

```typescript
import { template, renderHtml, templateExists, listTemplates } from '@stacksjs/email'

// From template file (welcome.stx or welcome.html)
const { html, text } = await template('welcome', {
  variables: { name: 'John', url: 'https://app.com/verify' },
  layout: 'default',     // wrap in layout (or false to skip)
  subject: 'Welcome'
})

// From raw HTML
const { html, text } = renderHtml('<h1>Hello {{ name }}</h1>', { name: 'John' })

// Check templates
templateExists('welcome')   // boolean
listTemplates()              // string[]
```

Variable syntax: `{{ variableName }}` (double braces with spaces).

## Example: Subscription Confirmation

```typescript
// app/Mail/SubscriptionConfirmation.ts
import { mail, template } from '@stacksjs/email'
import { url } from '@stacksjs/router'
import { config } from '@stacksjs/config'

export async function sendSubscriptionConfirmation({ to, subscriberUuid }: Options) {
  const { html, text } = await template('subscription-confirmation', {
    variables: {
      unsubscribeUrl: url('email.unsubscribe', { token: subscriberUuid }),
      appName: config.app.name
    }
  })

  await mail.send({
    from: { name: config.app.name, address: config.email.from.address },
    to,
    subject: 'Confirm your subscription',
    html,
    text
  })
}
```

## Using Mail in Actions/Events

```typescript
// In an action
import { sendWelcomeEmail } from '../../app/Mail/WelcomeEmail'

export default {
  name: 'SendWelcomeEmail',
  async handle(event: { email: string, name: string }) {
    await sendWelcomeEmail({ to: event.email, name: event.name })
    return { success: true }
  }
}
```

## The base design: email components

STX templates build on a bundled component library (`defaults/resources/components/Email`),
which every bundled template uses and which gives an app a finished look with a few props:

```html
<EmailLayout
  title="Your license key"
  preheader="Shown after the subject in the inbox"
  brand="Uplink" brandUrl="https://uplink.example" accent="#f59e0b"
  footer="You are getting this because you bought Uplink."
>
  <EmailText size="heading">You have Uplink Monthly.</EmailText>
  <EmailText>Here is your license key.</EmailText>
  <EmailCode>UPLK-7RKR-Z2BJ-V6QV-4PUH</EmailCode>
  <EmailButton href="https://uplink.example/activate" bg="#f59e0b" color="#18181b">Activate</EmailButton>
  <EmailDivider />
  <EmailText size="sm">Small print.</EmailText>
</EmailLayout>
```

- `<EmailLayout>`: a muted page with one bordered card (a border, not a shadow: Gmail strips
  shadows), an optional brand header and footer, a hidden `preheader`, and `@media` rules for
  dark mode and phones on the class hooks the other components carry. Clients that ignore
  `@media` keep the inline light design.
- `<EmailText size="heading|lg|md|sm">`, `<EmailButton>`, `<EmailDivider>`, `<EmailSection>`,
  `<EmailImage>`, `<EmailLink>`.
- `<EmailCode>`: a key or one-time code, large and monospace, selectable in one tap, wrapping at
  hyphens on a phone.

A colour passed as a prop is the template's own choice: that element gets no dark-mode hook.

Two stx traps when writing a component or template: never put the literal text of a style
tag inside a `<script server>` comment (the parser takes it for one), and write head CSS as a
raw string (`{!! css !!}`) rather than a style element, which stx lifts out as component CSS.

## Template Locations
- STX templates: `.stx` files processed by STX engine
- HTML templates: `.html` files with `{{ }}` variable interpolation
- Layouts: base HTML wrapping templates (header, footer, styles)

## Gotchas
- Mail classes are plain functions, not classes — no inheritance needed
- Templates support both `.stx` (reactive) and `.html` (static) formats
- Variable interpolation uses `{{ }}` — not `${}`
- `text` output is auto-generated from HTML via `htmlToText()`
- Layouts wrap the template content with shared structure (header/footer)
- For the email driver system (SES, SendGrid, etc.), see the `stacks-email` skill

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.