intermediate

How EmailingSimply Is Built

The technical stack behind EmailingSimply — Nuxt 3, Nuxt Content, Firebase, and how components are defined in Markdown.

Stack overview

EmailingSimply is built with:

  • Nuxt 3 (Nuxt 4 compatibility mode) — the frontend framework
  • Nuxt Content v2 — content management via Markdown files in /content
  • Firebase — authentication (Firebase Auth) and user data storage (Firestore)
  • Tailwind CSS — utility-first styling via PostCSS
  • Vercel — hosting and deployment

The source code lives in /app (Nuxt 4 app/ directory structure). Content lives in /content.


How components are defined

Each email component is a Markdown file in content/components/. The frontmatter defines all metadata; the html field contains the raw email HTML.

---
title: My Component
slug: my-component
description: What this component does
category: Hero
tags: [hero, cta]
premium: false
featured: false
darkModeSupport: partial   # none | partial | full
mobileSupport: full        # none | partial | full
outlookSupport: full       # none | partial | full
compatibility:
  gmailWeb: full
  gmailIos: full
  gmailAndroid: full
  appleMail: full
  appleMailIos: partial
  outlookWindows: full
  outlookMac: full
  outlookCom: full
  yahooMail: full
createdAt: "2025-08-01"
updatedAt: "2025-08-01"
html: |
  <table role="presentation" ...>
    ...
  </table>
---

Optional markdown notes about the component go here.

Token system

The HTML uses {{ tokenName }} placeholders that are replaced client-side by the customizer. Available tokens are defined in app/composables/useEmailCustomizer.ts:

TokenDefaultPurpose
background#f5f5f5Outer wrapper background
contentBg#ffffffContent area background
textColor#1a1a1aBody text color
accent#2563ebAccent / highlight color
buttonColor#2563ebCTA button background
buttonTextColor#ffffffCTA button text
borderColor#e5e5e5Borders and dividers
borderRadius4pxBorder radius on cards/buttons
fontFamilyArial, Helvetica, sans-serifFont stack
headingYour Heading HereHeading text placeholder
body(placeholder)Body copy placeholder
ctaTextShop NowCTA button label
ctaUrlhttps://example.comCTA button URL
spacing24pxVertical padding

Variants

A component can have multiple variants (e.g. Default, Dark background, Image background). Each variant needs a slug and can optionally include its own html:

variants:
  - name: Dark background
    slug: dark
    description: Dark version for dark-themed emails
    html: |
      <table ...>
        <!-- dark variant HTML -->
      </table>

If a variant has no html, clicking its tab shows the default HTML.


How guides and articles are defined

Guides live in content/guides/, articles in content/articles/. Both use Markdown with frontmatter:

---
title: Guide title
slug: guide-slug
description: Short description
difficulty: beginner   # beginner | intermediate | advanced
publishedAt: "2025-08-01"
tags: [tag1, tag2]
---

Guide content in Markdown...

Nuxt Content renders these with the ContentRenderer component, which uses Shiki for syntax highlighting.


Deployment

The app deploys automatically to Vercel on push to main. Environment variables for Firebase are set in Vercel project settings and injected via nuxt.config.ts runtimeConfig.public.

Firestore security rules must be updated separately in the Firebase Console.