Reading Emails
Emails are rendered as styled Markdown in the terminal using glamour. The reader supports vim-style navigation.
Navigation
| Key | Action |
|---|---|
j / k | scroll line by line |
space / d | page down / up |
gg | jump to top of email |
G | jump to bottom of email |
h / q / esc | back to inbox |
Opening Emails Externally
| Key | Action |
|---|---|
e | open in $EDITOR (read-only) — search, copy, vim motions |
o | open in w3m (terminal browser, clickable links) |
O | open in $BROWSER (GUI browser, images rendered) |
ctrl+o | open newsletter web version in $BROWSER (from List-Post header) |
Images
Remote images appear as [Image: alt] placeholders, keeping the reading experience clean and fast. To see images, press O to open in your browser.
Inline / attached images (e.g. screenshots pasted into an email) are listed in the reader header: Attach: [1] screenshot.png [2] report.pdf. Press 1–9 to download to ~/Downloads/ and open with xdg-open. Inline images also show [Image: filename.png] placeholders at their position in the body text.
When you press O to open in the browser, inline images are extracted from the email and saved to temp files. The HTML cid: references are rewritten to file:// paths so the browser renders them — including images sent by other people (not just your own).
Spy Pixel Blocking
neomd automatically detects and blocks tracking pixels, similar to HEY’s spy pixel blocker. Since the TUI renders emails as styled Markdown via glamour, remote images are never fetched — senders cannot tell if you read their email.
Two-layer detection (same approach as HEY):
- Curated denylist — 150+ tracking services with URL pattern matching, sourced from Simplify (BSD-3-Clause), LeaveMeAlone (CC-BY 3.0), and DHH’s original HEY list (MIT). Covers Mailchimp, HubSpot, SendGrid, ConvertKit, Substack, Amazon, Facebook, LinkedIn, and many more. When matched, the service name is shown (e.g. “Mailchimp”).
- Generic 1×1 pixel heuristic — catches custom/branded tracking domains not on the list by detecting
<img>tags with emptyaltAND tiny dimensions (both width/height 0–1) or CSS hiding (display:none). Layout spacers (e.g. 1×50) are not flagged.
When tracking pixels are detected, neomd shows:
°indicator in the inbox list (orange, next to the attachment@column)° N spy pixel(s) blocked (ServiceName)in the reader header with tracker attribution
Scanning: Spy pixels are detected when you read an email. To scan all emails in the current folder at once, press <space>S or run :scan-spy-pixels (alias :ssp). The scan runs in the background, skips already-scanned emails, and uses IMAP PEEK (won’t mark emails as read). Results are cached in ~/.cache/neomd/spy_pixels and persist across restarts.
When you press O to open in the browser, remote images load normally — you’re explicitly choosing to see the full email. Tracking pixel blocking is a TUI-level protection.
This is how it looks:
In overview:
![]()
And within an email open:
![]()
Links
Links in emails are automatically numbered inline where they appear in the body. A link like Check out our blog renders as Check out our blog [1] in the terminal.
Press space then a digit (1–9, 0 for 10th) to open the link in $BROWSER.
- Up to 10 links per email, deduplicated by URL
- Numbers appear inline so you can see them while reading without scrolling
- If an email has no links,
spaceworks as page-down as usual
Attachments
Attachments are listed in the reader header:
Attach: [1] report.pdf [2] photo.pngPress 1–9 to download attachment N to ~/Downloads/ and open it with xdg-open. Filenames are deduplicated automatically if a file already exists.
Download Raw Email Source
Press space then d in the reader to download the full raw email source (.eml file) to ~/Downloads/. The file is named neomd-YYYYMMDD-<subject>.eml using the email’s date and sanitized subject line.
This is useful for archiving emails, debugging headers, or importing into other email clients. The status bar shows a green confirmation when the download completes.
Threaded Inbox
Related emails are automatically grouped together in the inbox list. Threads are detected using a hybrid approach:
- Message-ID / In-Reply-To headers — proper RFC 2822 threading chain
- Subject + participant fallback — emails with the same normalized subject (stripped of
Re:,Fwd:, etc.) and overlapping participants (From/To) are grouped together
Threads display with a Twitter-style vertical connector line:
1 ·17:43 │ rafaelxxxxxxxxxxx@g… Re: Re: AUR Neomd (12K)
2 ·16:30 ·╰ rafaelxxxxxxxxxxx@g… Re: AUR Neomd (10K)
3 N 19:50 │ Bla blabla via Li… Jenna just messaged you (38K)
4 N 18:53 │ Bla blabla via Li… Jenna just messaged you (38K)
5 N 17:59 ╰ Bla blabla via Li… Jenna just messaged you (38K)
6 18:46 LinkedIn tom Weller replied to ... (45K)
7 ·14:22 · Simon Späti Data pipeline question (5K)·reply indicator — you’ve replied to this email (IMAP\Answeredflag, works across clients)·╰reply indicator within a thread│connects thread members (newest on top)╰marks the root/oldest email at the bottom of each thread°spy pixel indicator — tracking pixels were detected and blocked (shown after first read)- Non-threaded emails show no connector (clean, no visual noise)
- Threads are sorted by their most recent email, so active conversations float to the top
Or as image:

Replying, Forwarding, and Drafts
| Key | Action |
|---|---|
r | reply to sender |
ctrl+r | reply-all (sender + all CC recipients) |
f | forward email |
T | show full conversation thread across folders |
E | continue draft (only in Drafts folder) — re-opens as editable compose |
Conversation View
Press T from the inbox list or while reading an email to see the full conversation across folders. neomd searches Inbox, Sent, Archive, Waiting, and other configured folders for related emails — matching by normalized subject and participant overlap.
Results display in a temporary “Thread” tab:
- Each email shows
[Folder]prefix (e.g.[Sent],[Inbox],[Archive]) - Threading connectors (
│/╰) show the conversation structure - Press Enter to read any email, Esc to return to previous view
Also available as :thread (alias :t) from the command line.
Mark as Read Behavior
Neomd marks emails as read after you’ve spent time viewing them, not immediately when opened. This prevents accidental marking when quickly peeking at emails.
How it works:
- When you open an email (press
enterorl), neomd fetches the full body from IMAP - Once the body loads, a timer starts (default: 7 seconds)
- If you stay in the reader for the full duration, the email is marked as
\Seenon the server - If you exit early (press
h,q,esc, orT), the email stays unread
Configuration:
[ui]
mark_as_read_after_secs = 7 # wait 7 seconds (default)
# mark_as_read_after_secs = 0 # immediate marking (no timer)
# mark_as_read_after_secs = 10 # 10 secondsSet to 0 for immediate marking (as soon as the body finishes loading). Set to any value in seconds to customize the delay.
UI behavior:
- The local inbox list updates immediately when an email is marked as read — no need to manually refresh
- The unread indicator (
N) disappears as soon as marking completes - Manual toggle with
nstill works to mark/unmark emails at any time
Reply Indicator
Emails you’ve replied to show a · dot in the inbox list. This uses the standard IMAP \Answered flag, so it works across clients — if you reply from webmail, neomd shows it too.