Output format·Email·v1.0.0

email-template-builder

Build email template systems with React Email or MJML: responsive layouts, dark mode, multi-provider sending, and spam score optimization.

You say
Install this skill Read the source first Free Written by borghei · unverified publisher
Context cost
11.8k tokensestimated from the bundle, loaded when it triggers
Bundle
4 files · 47.3 kB3 scripts among them — read before you run
Licence
MIT + Commons Clausefree to use
Last change
v1.0.0
Servers it uses
Noneruns standalone

What it does

Build email template systems with React Email or MJML: responsive layouts, dark mode, multi-provider sending, and spam score optimization. Use when setting up transactional email, building email design systems, or debugging deliverability.

Installed, it changes the agent in these ways.

What this skill changes about the agent is not written down here yet. The listing was collected from its source, and the description is in its own SKILL.md.

Output format

Produces one artefact, exactly shaped.

emailtemplatesmjmltransactional
Filed under

Email

The skill itself

This is the whole product. A skill is instructions the model reads, so there is nothing behind the listing you cannot see first — the front matter loads with every session, and the body below it loads when the skill triggers.

SKILL.md22.9 kB · 607 lines
--- name: email-template-builder description: > Build email template systems with React Email or MJML: responsive layouts, dark mode, multi-provider sending, and spam score optimization. Use when setting up transactional email, building email design systems, or debugging deliverability. license: MIT + Commons Clause metadata: version: 1.0.0 author: borghei category: marketing domain: email-engineering tier: POWERFUL updated: 2026-03-09 frameworks: react-email, mjml, email-deliverability, responsive-email ---
18# Email Template Builder
19
20**Tier:** POWERFUL
21**Category:** Engineering / Marketing
22**Tags:** email templates, React Email, MJML, responsive email, deliverability, transactional email, dark mode
23
24## Overview
25
26Build complete transactional email systems: component-based templates with React Email or MJML, multi-provider sending abstraction, local preview with hot reload, i18n support, dark mode, spam optimization, and UTM tracking. Outputs production-ready code for any major email provider.
27
28This skill builds the email rendering and sending infrastructure. For writing email copy and designing sequences, use email-sequence.
29
30---
31
32## Clarify First
33
34Before building the templates, confirm these inputs. If any is unknown or vague, ASK — do not assume:
35
36- [ ] **Framework** — React Email or MJML, and whether the team uses React/TypeScript (decides the entire codebase; see the decision table below)
37- [ ] **Templates needed** — welcome, password reset, invoice, trial-expiring, digest, team-invite, etc. (determines which template files to scaffold)
38- [ ] **Sending provider** — Resend, SendGrid, Postmark, or SES (selects which provider adapter to build)
39- [ ] **Locales & dark mode** — required languages and dark-mode support (drives the i18n system and base-layout styles)
40
41Stop rule: ask only the 2-3 that most change the output. If the user says "just draft it," proceed and list your assumptions at the top of the artifact.
42
43## Architecture Decision: React Email vs MJML
44
45| Factor | React Email | MJML |
46|--------|-----------|------|
47| **Component reuse** | Full React component model | Partial (mj-attributes) |
48| **TypeScript** | Native | Requires build step |
49| **Preview server** | Built-in (email dev) | Requires separate setup |
50| **Email client compatibility** | Good (renders to tables) | Excellent (battle-tested) |
51| **Dark mode** | CSS media queries | CSS media queries |
52| **Learning curve** | Low (if you know React) | Low (HTML-like syntax) |
53| **Best for** | Teams already using React | Maximum email client compat |
54
55**Recommendation:** React Email for TypeScript teams shipping SaaS. MJML for marketing teams needing maximum compatibility across Outlook, Gmail, Apple Mail, and legacy clients.
56
57---
58
59## Project Structure
60
61```
62emails/
63├── components/
64│ ├── layout/
65│ │ ├── base-layout.tsx # Shared wrapper: header, footer, styles
66│ │ ├── button.tsx # CTA button component
67│ │ └── divider.tsx # Styled horizontal rule
68│ ├── blocks/
69│ │ ├── hero.tsx # Hero section with heading + text
70│ │ ├── feature-row.tsx # Icon + text feature highlight
71│ │ ├── testimonial.tsx # Quote + attribution
72│ │ └── pricing-table.tsx # Plan comparison
73├── templates/
74│ ├── welcome.tsx # Welcome / confirm email
75│ ├── password-reset.tsx # Password reset link
76│ ├── invoice.tsx # Payment receipt / invoice
77│ ├── trial-expiring.tsx # Trial expiration warning
78│ ├── weekly-digest.tsx # Activity summary
79│ └── team-invite.tsx # Team invitation
80├── lib/
81│ ├── send.ts # Unified send function
82│ ├── providers/
83│ │ ├── resend.ts # Resend adapter
84│ │ ├── sendgrid.ts # SendGrid adapter
85│ │ ├── postmark.ts # Postmark adapter
86│ │ └── ses.ts # AWS SES adapter
87│ ├── tracking.ts # UTM parameter injection
88│ └── render.ts # Template rendering
89├── i18n/
90│ ├── en.ts # English strings
91│ ├── de.ts # German strings
92│ └── types.ts # Typed translation keys
93└── package.json
94```
95
96---
97
98## Base Layout Component
99
100```tsx
101// emails/components/layout/base-layout.tsx
102import {
103 Body, Container, Head, Html, Img, Preview,
104 Section, Text, Hr, Font
105} from "@react-email/components";
106
107interface BaseLayoutProps {
108 preview: string;
109 locale?: string;
110 children: React.ReactNode;
111}
112
113export function BaseLayout({ preview, locale = "en", children }: BaseLayoutProps) {
114 return (
115 <Html lang={locale}>
116 <Head>
117 <Font
118 fontFamily="Inter"
119 fallbackFontFamily="Arial"
120 webFont={{
121 url: "https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hiJ-Ek-_EeA.woff2",
122 format: "woff2",
123 }}
124 fontWeight={400}
125 fontStyle="normal"
126 />
127 <style>{`
128 @media (prefers-color-scheme: dark) {
129 .email-body { background-color: #111827 !important; }
130 .email-container { background-color: #1f2937 !important; }
131 .email-text { color: #e5e7eb !important; }
132 .email-heading { color: #f9fafb !important; }
133 .email-muted { color: #9ca3af !important; }
134 }
135 @media only screen and (max-width: 600px) {
136 .email-container { width: 100% !important; padding: 16px !important; }
137 }
138 `}</style>
139 </Head>
140 <Preview>{preview}</Preview>
141 <Body className="email-body" style={body}>
142 <Container className="email-container" style={container}>
143 <Section style={header}>
144 <Img
145 src={${process.env.ASSET_URL}/logo.png}
146 width={120} height={36} alt="[Product]"
147 />
148 </Section>
149 <Section style={content}>{children}</Section>
150 <Hr className="email-muted" style={divider} />
151 <Section style={footer}>
152 <Text className="email-muted" style={footerText}>
153 [Company] Inc. - [Address]
154 </Text>
155 <Text className="email-muted" style={footerText}>
156 <a href="{{unsubscribe_url}}" style={link}>Unsubscribe</a>
157 {" | "}
158 <a href="{{preferences_url}}" style={link}>Email Preferences</a>
159 {" | "}
160 <a href="{{privacy_url}}" style={link}>Privacy Policy</a>
161 </Text>
162 </Section>
163 </Container>
164 </Body>
165 </Html>
166 );
167}
168
169// Styles (inline for email client compatibility)
170const body = { backgroundColor: "#f3f4f6", fontFamily: "Inter, Arial, sans-serif", margin: 0, padding: "40px 0" };
171const container = { maxWidth: "600px", margin: "0 auto", backgroundColor: "#ffffff", borderRadius: "8px", overflow: "hidden" };
172const header = { padding: "24px 32px", borderBottom: "1px solid #e5e7eb" };
173const content = { padding: "32px" };
174const divider = { borderColor: "#e5e7eb", margin: "0 32px" };
175const footer = { padding: "24px 32px" };
176const footerText = { fontSize: "12px", color: "#6b7280", textAlign: "center" as const, margin: "4px 0", lineHeight: "1.6" };
177const link = { color: "#6b7280", textDecoration: "underline" };
178```
179
180---
181
182## Template Examples
183
184### Welcome Email
185
186```tsx
187// emails/templates/welcome.tsx
188import { Button, Heading, Text } from "@react-email/components";
189import { BaseLayout } from "../components/layout/base-layout";
190
191interface WelcomeProps {
192 name: string;
193 confirmUrl: string;
194 trialDays?: number;
195}
196
197export default function Welcome({ name, confirmUrl, trialDays = 14 }: WelcomeProps) {
198 return (
199 <BaseLayout preview={Welcome, ${name}! Confirm your email to get started.}>
200 <Heading className="email-heading" style={h1}>
201 Welcome to [Product], {name}
202 </Heading>
203 <Text className="email-text" style={text}>
204 You have {trialDays} days to explore everything -- no credit card required.
205 Confirm your email to activate your account:
206 </Text>
207 <Button href={confirmUrl} style={button}>
208 Confirm Email Address
209 </Button>
210 <Text className="email-muted" style={muted}>
211 Button not working? Paste this link in your browser:{" "}
212 <a href={confirmUrl} style={linkStyle}>{confirmUrl}</a>
213 </Text>
214 </BaseLayout>
215 );
216}
217
218const h1 = { fontSize: "24px", fontWeight: "700", color: "#111827", margin: "0 0 16px", lineHeight: "1.3" };
219const text = { fontSize: "16px", lineHeight: "1.6", color: "#374151", margin: "0 0 24px" };
220const button = { backgroundColor: "#4f46e5", color: "#ffffff", borderRadius: "6px", fontSize: "16px", fontWeight: "600", padding: "12px 24px", textDecoration: "none", display: "inline-block" };
221const muted = { fontSize: "13px", color: "#6b7280", marginTop: "24px", lineHeight: "1.5" };
222const linkStyle = { color: "#4f46e5", wordBreak: "break-all" as const };
223```
224
225### Invoice Email
226
227```tsx
228// emails/templates/invoice.tsx
229import { Row, Column, Section, Heading, Text, Hr, Button } from "@react-email/components";
230import { BaseLayout } from "../components/layout/base-layout";
231
232interface LineItem { description: string; amount: number; }
233
234interface InvoiceProps {
235 name: string;
236 invoiceNumber: string;
237 date: string;
238 dueDate: string;
239 items: LineItem[];
240 total: number;
241 currency?: string;
242 downloadUrl: string;
243}
244
245export default function Invoice({
246 name, invoiceNumber, date, dueDate, items,
247 total, currency = "USD", downloadUrl,
248}: InvoiceProps) {
249 const fmt = new Intl.NumberFormat("en-US", { style: "currency", currency });
250
251 return (
252 <BaseLayout preview={Invoice ${invoiceNumber} -- ${fmt.format(total / 100)}}>
253 <Heading className="email-heading" style={h1}>
254 Invoice #{invoiceNumber}
255 </Heading>
256 <Text className="email-text" style={text}>Hi {name},</Text>
257 <Text className="email-text" style={text}>
258 Here is your invoice. Thank you for your business.
259 </Text>
260
261 {/* Meta row */}
262 <Section style={metaBox}>
263 <Row>
264 <Column>
265 <Text style={metaLabel}>Invoice Date</Text>
266 <Text style={metaValue}>{date}</Text>
267 </Column>
268 <Column>
269 <Text style={metaLabel}>Due Date</Text>
270 <Text style={metaValue}>{dueDate}</Text>
271 </Column>
272 <Column>
273 <Text style={metaLabel}>Amount Due</Text>
274 <Text style={metaValueBold}>{fmt.format(total / 100)}</Text>
275 </Column>
276 </Row>
277 </Section>
278
279 {/* Line items */}
280 {items.map((item, i) => (
281 <Row key={i} style={i % 2 === 0 ? rowEven : rowOdd}>
282 <Column><Text style={cell}>{item.description}</Text></Column>
283 <Column><Text style={cellRight}>{fmt.format(item.amount / 100)}</Text></Column>
284 </Row>
285 ))}
286 <Hr style={divider} />
287 <Row>
288 <Column><Text style={totalLabel}>Total</Text></Column>
289 <Column><Text style={totalValue}>{fmt.format(total / 100)}</Text></Column>
290 </Row>
291
292 <Button href={downloadUrl} style={button}>
293 Download PDF
294 </Button>
295 </BaseLayout>
296 );
297}
298
299const h1 = { fontSize: "24px", fontWeight: "700", color: "#111827", margin: "0 0 16px" };
300const text = { fontSize: "15px", lineHeight: "1.6", color: "#374151", margin: "0 0 12px" };
301const metaBox = { backgroundColor: "#f9fafb", borderRadius: "8px", padding: "16px", margin: "16px 0" };
302const metaLabel = { fontSize: "11px", color: "#6b7280", fontWeight: "600", textTransform: "uppercase" as const, margin: "0 0 4px", letterSpacing: "0.05em" };
303const metaValue = { fontSize: "14px", color: "#111827", margin: "0" };
304const metaValueBold = { fontSize: "18px", fontWeight: "700", color: "#4f46e5", margin: "0" };
305const rowEven = { backgroundColor: "#ffffff" };
306const rowOdd = { backgroundColor: "#f9fafb" };
307const cell = { fontSize: "14px", color: "#374151", padding: "10px 12px" };
308const cellRight = { ...cell, textAlign: "right" as const };
309const divider = { borderColor: "#e5e7eb", margin: "8px 0" };
310const totalLabel = { fontSize: "16px", fontWeight: "700", color: "#111827", padding: "8px 12px" };
311const totalValue = { ...totalLabel, textAlign: "right" as const };
312const button = { backgroundColor: "#4f46e5", color: "#ffffff", borderRadius: "6px", padding: "12px 24px", fontSize: "15px", fontWeight: "600", textDecoration: "none", display: "inline-block", marginTop: "16px" };
313```
314
315---
316
317## Multi-Provider Send Abstraction
318
319```typescript
320// emails/lib/send.ts
321import { render } from "@react-email/render";
322
323interface EmailPayload {
324 to: string;
325 subject: string;
326 template: React.ReactElement;
327 tags?: Record<string, string>;
328}
329
330interface EmailProvider {
331 send(payload: { to: string; subject: string; html: string; text: string; tags?: Record<string, string> }): Promise<{ id: string }>;
332}
333
334// Provider factory
335function getProvider(): EmailProvider {
336 const provider = process.env.EMAIL_PROVIDER || "resend";
337 switch (provider) {
338 case "resend": return require("./providers/resend").default;
339 case "sendgrid": return require("./providers/sendgrid").default;
340 case "postmark": return require("./providers/postmark").default;
341 case "ses": return require("./providers/ses").default;
342 default: throw new Error(Unknown email provider: ${provider});
343 }
344}
345
346export async function sendEmail(payload: EmailPayload) {
347 const html = addTracking(render(payload.template), { campaign: payload.tags?.type || "transactional" });
348 const text = render(payload.template, { plainText: true });
349
350 return getProvider().send({
351 to: payload.to,
352 subject: payload.subject,
353 html,
354 text,
355 tags: payload.tags,
356 });
357}
358```
359
360---
361
362## UTM Tracking Injection
363
364```typescript
365// emails/lib/tracking.ts
366interface TrackingConfig {
367 campaign: string;
368 source?: string;
369 medium?: string;
370}
371
372export function addTracking(html: string, config: TrackingConfig): string {
373 const params = new URLSearchParams({
374 utm_source: config.source || "email",
375 utm_medium: config.medium || "transactional",
376 utm_campaign: config.campaign,
377 }).toString();
378
379 // Add UTM to all internal links (skip unsubscribe and external)
380 return html.replace(
381 /href="(https?:\/\/(?:www\.)?yourdomain\.com[^"]*?)"/g,
382 (match, url) => {
383 const sep = url.includes("?") ? "&" : "?";
384 return href="${url}${sep}${params}";
385 }
386 );
387}
388```
389
390---
391
392## i18n System
393
394```typescript
395// emails/i18n/types.ts
396export interface EmailStrings {
397 welcome: {
398 preview: (name: string) => string;
399 heading: (name: string) => string;
400 body: (days: number) => string;
401 cta: string;
402 fallbackLink: string;
403 };
404 invoice: {
405 preview: (number: string, amount: string) => string;
406 heading: (number: string) => string;
407 greeting: (name: string) => string;
408 downloadCta: string;
409 };
410 common: {
411 unsubscribe: string;
412 preferences: string;
413 privacy: string;
414 };
415}
416
417// emails/i18n/en.ts
418import type { EmailStrings } from "./types";
419export const en: EmailStrings = {
420 welcome: {
421 preview: (name) => Welcome, ${name}! Confirm your email to get started.,
422 heading: (name) => Welcome to [Product], ${name},
423 body: (days) => You have ${days} days to explore everything -- no credit card required.,
424 cta: "Confirm Email Address",
425 fallbackLink: "Button not working? Paste this link in your browser:",
426 },
427 // ... other templates
428};
429
430// emails/i18n/de.ts
431import type { EmailStrings } from "./types";
432export const de: EmailStrings = {
433 welcome: {
434 preview: (name) => Willkommen, ${name}! Bestaetigen Sie Ihre E-Mail.,
435 heading: (name) => Willkommen bei [Product], ${name},
436 body: (days) => Sie haben ${days} Tage Zeit, alles zu erkunden -- keine Kreditkarte noetig.,
437 cta: "E-Mail-Adresse bestaetigen",
438 fallbackLink: "Button funktioniert nicht? Fuegen Sie diesen Link in Ihren Browser ein:",
439 },
440 // ... other templates
441};
442```
443
444---
445
446## Deliverability Checklist
447
448### DNS Records (Required)
449
450- [ ] **SPF**: v=spf1 include:_spf.provider.com ~all on sending domain
451- [ ] **DKIM**: Provider-specific CNAME records configured
452- [ ] **DMARC**: v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com
453- [ ] **Return-Path**: Matches sending domain (not provider default)
454
455### Content Rules
456
457- [ ] Sender uses own domain (not @gmail.com)
458- [ ] Subject under 50 characters, no ALL CAPS, no spam triggers
459- [ ] Text-to-image ratio: minimum 60% text
460- [ ] Plain text version included alongside HTML
461- [ ] Unsubscribe link in every email (CAN-SPAM, GDPR, one-click)
462- [ ] Physical mailing address in footer (CAN-SPAM requirement)
463- [ ] No URL shorteners (use full branded links)
464- [ ] Single primary CTA per email
465- [ ] All images have alt text
466- [ ] HTML validates (no broken/unclosed tags)
467
468### Infrastructure
469
470- [ ] Separate sending domains for transactional vs marketing
471- [ ] Warm up new sending domains gradually (start with 50/day, increase 2x weekly)
472- [ ] Monitor bounce rates (<2% hard bounces)
473- [ ] Process bounces and complaints automatically
474- [ ] Test with Mail-Tester.com before production sends (target: 9+/10)
475
476---
477
478## Email Client Compatibility
479
480### Known Quirks
481
482| Client | Quirk | Workaround |
483|--------|-------|-----------|
484| Outlook (Windows) | No CSS grid/flexbox, ignores margin on images | Use <table> layout (React Email handles this) |
485| Gmail | Strips <head> styles, limits CSS | Inline all styles (React Email handles this) |
486| Apple Mail | Best support, renders dark mode well | Standard approach works |
487| Yahoo Mail | Limited CSS support | Avoid advanced selectors |
488| Outlook.com | Strips background images | Use background-color as fallback |
489
490### Testing Matrix
491
492Test every template on these clients before production:
493
494| Priority | Client | Method |
495|----------|--------|--------|
496| Critical | Gmail (web) | Send test email |
497| Critical | Apple Mail (iOS) | Send test email |
498| Critical | Outlook (Windows, latest) | Litmus or Email on Acid |
499| High | Outlook.com (web) | Send test email |
500| High | Gmail (Android) | Send test email |
501| Medium | Yahoo Mail | Litmus |
502| Medium | Outlook (Mac) | Send test email |
503
504---
505
506## Dev Workflow
507
508```bash
509# Start preview server with hot reload
510npx email dev --dir emails/templates --port 3001
511
512# Export to static HTML (for testing with Litmus/Email on Acid)
513npx email export --dir emails/templates --outDir emails/dist
514
515# Send test email
516npx tsx emails/lib/send-test.ts --template welcome --to test@example.com
517
518# Validate HTML
519npx email lint --dir emails/templates
520```
521
522---
523
524## Common Pitfalls
525
526| Pitfall | Consequence | Prevention |
527|---------|-------------|------------|
528| Using CSS grid/flexbox | Layout breaks in Outlook | Use Row/Column from React Email (renders to tables) |
529| Container wider than 600px | Breaks on Gmail mobile | Max-width: 600px on container |
530| Missing plain text version | Lower deliverability score | Always generate plain text with render(template, { plainText: true }) |
531| Same domain for transactional + marketing | Marketing complaints tank transactional delivery | Separate sending domains/subdomains |
532| Skipping email warm-up | Emails go to spam | Start low, increase gradually over 2-4 weeks |
533| Dark mode ignoring | Unreadable emails for 30%+ of users | Add prefers-color-scheme: dark media queries with !important |
534
535---
536
537## Related Skills
538
539| Skill | Use When |
540|-------|----------|
541| **email-sequence** | Writing email copy and designing automation flows |
542| **analytics-tracking** | Setting up email engagement tracking and attribution |
543| **launch-strategy** | Coordinating email templates for product launches |
544
545---
546
547## Troubleshooting
548
549| Symptom | Likely Cause | Fix |
550|---------|-------------|-----|
551| Email clipped in Gmail | HTML over 102KB | Run render_size_analyzer.py. Remove comments, minify, replace base64 images. |
552| Layout broken in Outlook | CSS flexbox/grid used | Use table-based layout. Run template_validator.py for compatibility check. |
553| Styles stripped in Gmail | Styles in <head> only | Inline all CSS. React Email handles this automatically. |
554| Unreadable in dark mode | No dark mode CSS | Add prefers-color-scheme: dark media queries with !important. |
555| Low deliverability score | Missing unsubscribe, heavy images | Run spam_score_checker.py. Add RFC 8058 one-click unsubscribe headers. |
556| Images not loading | Blocked by email client defaults | Add descriptive alt text. Maintain 60%+ text-to-image ratio. |
557| Template renders differently across clients | Unsupported CSS properties | Test on Gmail, Apple Mail, Outlook (Windows) before production sends. |
558
559---
560
561## Success Criteria
562
563- Spam score of 9+/10 on mail-tester.com before production sends
564- Template renders correctly on Gmail, Apple Mail, and Outlook (Windows)
565- HTML under 80KB (well under Gmail's 102KB clip threshold)
566- Text-to-image ratio above 60%
567- Dark mode tested and readable for 30%+ of users
568- All images have alt text and explicit width/height dimensions
569- One-click unsubscribe (RFC 8058) implemented in all templates
570- Separate sending domains for transactional vs. marketing email
571
572---
573
574## Scope & Limitations
575
576**In Scope:** Email HTML/CSS template engineering, React Email and MJML components, multi-provider sending abstraction, i18n, dark mode, deliverability infrastructure, spam score optimization.
577
578**Out of Scope:** Email copy/sequence writing (use email-sequence), marketing automation workflows, email list management, A/B test statistical analysis.
579
580---
581
582## Python Automation Tools
583
584### 1. Spam Score Checker (scripts/spam_score_checker.py)
585Analyzes email HTML for spam risk: text-to-image ratio, link density, spam words, unsubscribe presence, HTML structure.
586
587```bash
588python scripts/spam_score_checker.py template.html
589python scripts/spam_score_checker.py template.html --json
590```
591
592### 2. Template Validator (scripts/template_validator.py)
593Validates email templates for client compatibility (Outlook, Gmail), accessibility, responsive design, and inline styles.
594
595```bash
596python scripts/template_validator.py template.html
597python scripts/template_validator.py template.html --json
598```
599
600### 3. Render Size Analyzer (scripts/render_size_analyzer.py)
601Analyzes template file size, estimates render weight, and checks against Gmail's 102KB clip threshold with detailed breakdown.
602
603```bash
604python scripts/render_size_analyzer.py template.html
605python scripts/render_size_analyzer.py --dir templates/ --json
606```
607
In the file
SKILL.md2,821 words
Files4
LicenceMIT + Commons Clause
Why you can read it

Nothing in a skill executes. The client loads the text and the model follows it, so a skill can be audited the way a runbook is — by reading it.

What it costs in context

Skills are not billed by the call. They are paid for in context: every token the instructions occupy is a token your code, your diff and your conversation cannot use. Here is what this one takes and when it takes it.

≈130
always loaded
The name and description, so the model knows the skill exists and when to reach for it.
11,695
on trigger
The instruction body and 3 supporting files, read only when the skill fires.
5.9%
of a 200k window
Ten skills this size would take about 59% of the window before you open a file.
050k100k150k200k context window

11.8k tokens, estimated from the bundle at four bytes to the token, held for the rest of the session once it triggers. Heavy. Teams tend to install this one per project rather than globally, and load it only when the job comes up.

Servers bill, skills cost

A server charges by the month. A skill charges once per session, in context, and then keeps charging it for as long as the session lives.

Before and after

The same question, put to the same model twice: once as it comes, and once with these instructions loaded.

No worked example has been published for this skill yet.

Adoption
Installsnone yet
Ratingno reviews yet

The procedure it runs

The procedure has not been published here. It is in the skill’s own SKILL.md, which its author has not sent to the marketplace yet.

Prose, not code

These steps are written for a model to follow, not executed by a runtime. It can still be told to skip one, and it will say so when it does.

Servers it uses

None. This skill calls no MCP servers at all.

Everything it needs is in the instructions, so it works in a project with nothing connected — the model reads the file and changes how it works with what it can already reach.

It writes no files and reaches no network. All it changes is how the model reasons and writes.

What it asks for
Writes filesno
Network accessno

Read from the allowed-tools line of this skill’s own SKILL.md. A skill grants no permissions of its own — it can only ask for tools your client already has.

What it will not do

Every skill is narrow, and the useful ones say where they stop. These are the jobs this one is the wrong tool for.

What this skill is not for has not been published here. Nothing is implied by that: it is a section the author has not filled in.

If this is not it

13 other skills in Email

What is in the bundle

4 files, 47.3 kB on disk. Mostly text — the instructions the model reads — with 3 scripts in it that your client would run only if the instructions tell it to.

  • SKILL.md22.9 kB
  • scripts/render_size_analyzer.py6.1 kB
  • scripts/spam_score_checker.py10.2 kB
  • scripts/template_validator.py8.1 kB
What is not in it

A skill installs nothing and depends on nothing: it is a folder your client reads. This one carries 3 scripts beside the text, so the bundle is 4 files you can review in full before installing. The MIT + Commons Clause licence covers the templates and examples as well as the instructions.

Install

Installing copies the bundle into your project. Nothing runs at install time — the files sit on disk until the model reads them.

# email-template-builder · 11.8k tokens when loaded npx mcprush@latest skill add borghei/email-template-builder

Writes to .claude/skills/email-template-builder/ in the current project. Add --global to put it in your home directory instead, for every project.

Which clients pick it up on their own

A skill is a folder of text. A client with a skills folder reads it without being told; everywhere else the same text works, it is just handed to the model rather than found.

Claude Code.claude/skills/
Claude Desktop
ChatGPT
Cursor.cursor/skills/
VS Code.github/skills/
Codex CLI.agents/skills/
Gemini CLI.gemini/skills/
Grok.grok/skills/
Zed.agents/skills/
Windsurf.windsurf/skills/
Agent SDK.claude/skills/
HTTP / API
This release
Version1.0.0
Publishedno release date on file
PriceFree
Referenceborghei/email-template-builder

Versions

v1.0.0 is what is on the shelf; no release here carries a date. Instructions change more often than APIs do — a skill can be rewritten entirely without anything it depends on moving.

v1.0.0
  • No earlier releases have been published to the marketplace.
Pinning

Put borghei/email-template-builder@1.0.0 in the install command to hold this exact version. Without the suffix you get whatever is current the day you install, and nothing moves under you afterwards.

Reviews

no reviews yet · no installs yet

Nobody has reviewed this skill yet. The rating is the mean of the reviews written here, so there is none until somebody writes the first.

Who can post

Only accounts that have had the skill installed for fourteen days, so a review is written after living with it rather than after reading it. Publishers may reply once.

Who wrote it

BO
borghei

Publishes on mcprush.

0 servers listed2 skills listednot claimed
Profile
Publisher
Servers0