An easy-to-use template management add-on for Mozilla Thunderbird (128+).
Save and reuse email templates — including file attachments — directly from the compose window.
| Template selection | Context menu |
|---|---|
![]() |
![]() |
| Manage templates | Template editor |
|---|---|
![]() |
![]() |
- Rich text editor — Create templates with bold, italic, underline, lists, headings, and quotes
- File attachments — Attach files to templates; they are stored with the template and added automatically when inserting
- One-click insert — Insert templates via the toolbar button in the compose window or the right-click context menu
- Subject, recipients & insertion mode — Set a default subject, To/Cc/Bcc addresses, and choose whether the template appends to or replaces the current message body
- Categories — Organise templates into categories and filter by them in both the popup and the options page
- Variables — Use
{DATE},{TIME},{DATETIME},{YEAR},{WEEKDAY},{SENDER_NAME},{SENDER_EMAIL},{ACCOUNT_NAME},{ACCOUNT_EMAIL},{RECIPIENT_NAME},{RECIPIENT_FIRSTNAME},{RECIPIENT_NICKNAME},{RECIPIENT_EMAIL},{REPLY_QUOTE}, and{LAST_MESSAGE_SUBJECT}in subject or body; they are resolved on insert. Click a variable chip in the editor to insert it at the cursor - Nickname from the address book —
{RECIPIENT_NICKNAME}fills in the Nickname field of the matching contact, so the contact card can stay formal while the greeting uses the name the person actually goes by. It needs the optionaladdressBookspermission, which TemplateWing asks for once from the options page — until then, and for contacts without a nickname, the variable resolves to nothing. Pair it with a conditional for a safe fallback:{IF recipient.nickname!=""}Hi {RECIPIENT_NICKNAME},{ELSE}Dear {RECIPIENT_FIRSTNAME},{ENDIF} - Names that read like names — A recipient with no display name (
julia.kalder@example.com) used to be greeted as "julia.kalder".{RECIPIENT_NAME}and{RECIPIENT_FIRSTNAME}now come from the matching address-book contact where there is one, and are otherwise read out of the address itself ("Julia Kalder" / "Julia"). A display name that came with the recipient is never overwritten - Recipients are added, not overwritten — A template that carries its own To/Cc/Bcc entries adds them to whoever is already in the compose window instead of replacing them, the same way its body and attachments add. The person you picked by hand stays, and stays first — which is also the entry the
{RECIPIENT_*}variables resolve against - Asks who the message is for — Variables are resolved once, when the template is inserted. If a template greets the recipient and neither the window nor the template supplies an address, TemplateWing asks for one before writing the text, in the same dialog as
{PROMPT}/{CHOICE}. Leaving it blank is a valid answer and keeps the fallback wording - Resolve again — Picked the recipient after inserting? The popup offers to run the last inserted template again against the recipients the window has now, replacing the message text. Two clicks, because it cannot be undone from there
- Conditional content —
{IF lhs=="value"}…{ELSE}…{ENDIF}blocks let templates branch onrecipient.domain,recipient.firstname,recipient.nickname,identity.email, etc. Nesting is supported; unknown variables compare as the empty string - Ask-on-insert prompts —
{PROMPT:Label:default}and{CHOICE:Label:opt1|opt2|opt3}open a small dialog at insert time and substitute the user's answer - Favorites / pinning — Click the ★ on any template to keep it pinned to the top of the popup, regardless of recent-use sort
- Popup search — Autofocused search box filters templates instantly across name, subject, and category; Enter inserts the top match, Esc clears
- Default template per account — In the options page, pick a template to auto-insert whenever you open a new compose window with a given account (replies and forwards are skipped)
- Per-row import preview — Importing a JSON file now opens a preview table where every incoming template gets its own action (Skip / Add as copy / Replace existing / Edit name); bulk-action buttons cover the common cases
- Usage dashboard — New "Usage" tab with a sortable table, a top-10 inline SVG bar chart, "unused since 30/90/365 days / never" filter, and RFC-4180 CSV export
- Template linter — Inline badges flag unknown variables, broken nested-template references, cycles, oversize attachments, and invalid recipients; a one-line summary at the top of the list tells you whether everything is clean
- Keyboard shortcuts — Insert your first 9 templates with
Ctrl+Shift+1–Ctrl+Shift+9directly from the compose window - Recent templates first — The popup sorts by most recently used, so your go-to templates are always at the top; the options page shows a usage count per template
- Import / Export with merge modes — Back up all templates as a JSON file and restore or share them on any device. On import, choose to add all, skip duplicates, or update existing by name, with a pre-import validation summary
- Recipient validation — To, Cc, and Bcc fields are validated on save so typos are caught before insertion
- Paste sanitization — Toggle "Paste as plain text" in the editor toolbar to strip formatting from pasted content
- Duplicate name check — Prevents saving a template with the same name as an existing one
- Attachment guardrails — Large-file warnings per attachment and total size indicator; per-file error handling during insertion
- Storage schema versioning — Automatic data migration keeps templates intact across add-on updates
- Save from email — Right-click any message in the message list → Save as Template to create a template pre-filled with subject and body
- Multi-select & bulk actions — Tick several templates in the options list, then change their category, export just that selection, or delete them in one go. "Select all" applies to whatever the current search/category filter shows
- Welcome tab — Opens once after installation with a short overview of the features, how to create a first template, and links to the project page, the issue tracker, and a donation page. It does not reappear on updates
- Dark mode — Follows the system colour scheme automatically
- Localized — Fully localized in English, German, French, Spanish, Italian, Portuguese, and Dutch.
npm run lintenforces that every locale carries every key
- Visit the TemplateWing page on Thunderbird Add-ons
- Click Add to Thunderbird
- Download the latest
.xpifile from the Releases page - In Thunderbird: Menu (≡) → Add-ons and Themes (
Ctrl+Shift+A) - Gear icon (⚙) → Install Add-on From File… → select the
.xpifile
- Open a new compose window
- Click the TemplateWing icon in the toolbar to see your templates
- Click Insert to apply a template — or use
Ctrl+Shift+1–9for the first nine - Click Manage Templates… to create, edit, or delete templates
Templates can also be inserted via the right-click context menu in the compose body.
To create a template from an existing email, right-click a message in the message list and choose Save as Template.
- Open Thunderbird → Menu (≡) → Add-ons and Themes (
Ctrl+Shift+A) - Gear icon (⚙) → Debug Add-ons
- Load Temporary Add-on… → select
manifest.json - Click Inspect next to the add-on for console/debugging
Reload after changes by clicking Reload on the Debug Add-ons page (no restart needed).
Portable Node-based builder (same file list as CI). Needs Node.js 20+ and the zip CLI — on Windows that means Git Bash or WSL:
npm run build:xpiThis creates templatewing-<version>.xpi in the parent directory and prints the SHA-256.
The build is byte-reproducible: the same commit yields the same SHA-256 on any machine, in any timezone, under any umask, so a published hash can be verified rather than trusted. Files are staged and stamped with a fixed timestamp and mode, zip -X drops the timestamp/uid extra fields, TZ is pinned to UTC, and .gitattributes pins line endings to LF. Set SOURCE_DATE_EPOCH to build against a different fixed date. CI verifies all of this on every push and fails if REVIEW_NOTE.txt quotes a stale hash.
Tags pushed to GitHub are built and released automatically by .github/workflows/release.yml, which attaches both the XPI and the source archive. Use an annotated tag and write the release notes into its message — the workflow lifts that message into the release description, so the notes exist at the moment the release is cut rather than being added afterwards:
git tag -a v2.9.0 -m "TemplateWing 2.9.0
What changed, in the words a user would read."
git push origin v2.9.0The subject line is dropped (the release already carries the version) and the body becomes the description. A lightweight tag falls back to a link to the commit history.
The archive submitted to Thunderbird Add-ons alongside the XPI:
npm run build:sourceIt contains every file in the XPI plus the build scripts, both test suites, and the CI workflows. The file list is derived from scripts/xpi-files.mjs, the same module build:xpi reads, so the two cannot drift apart. CI builds both archives on every push and verifies that the source archive rebuilds identical XPI contents.
The legacy PowerShell scripts build-xpi.ps1 and build-source-zip.ps1 now just delegate to the Node builders and are kept only for backwards compatibility.
manifest.json — Extension manifest (Manifest V2)
background.html — Background page (loads background.js)
background.js — Context menu, storage listeners
modules/template-store.js — CRUD operations over storage.local
modules/template-insert.js — Variable replacement, nested templates, insertion
modules/address-book.js — Nickname lookup (optional addressBooks permission)
modules/message-utils.js — Recipient parsing/merging, quoting, subject cleanup
modules/validation.js — Recipient/import validation helpers
prompt-dialog/ — Ask-on-insert dialog ({PROMPT}, {CHOICE}, recipient)
popup/popup.html|css|js — Compose-action popup (template list & insert)
options/options.html|css|js — Options page (template editor)
images/ — Extension icons (SVG source + 16/32/64/128 PNG)
_locales/{en,de,fr,es,it,pt,nl}/ — Localization strings
tests/ — Unit tests (Node.js built-in test runner)
No transpiler, no bundler, no runtime dependencies — just vanilla ES6 modules. The packaging step only zips the files listed in scripts/xpi-files.mjs; the code in the XPI is the source as-is.
TemplateWing stores all data locally on your device using Thunderbird's built-in storage API. No data is collected, transmitted to external servers, or shared with third parties.
Found a bug or have an idea? Open an issue — constructive feedback is always welcome!



