EML File Viewer — Address Card, Both Bodies, and Every Enclosed File
A saved message is a stack of parts, not a single sheet. A useful EML file viewer shows the address lines a person actually needs, both body variants, pictures that travelled inside the file, and a row for every enclosed byte. The box below is that surface. The notes under it stick to what the parser and the viewer component really do.
Drop .eml, .emlx, .msg or .mbox files here — or click to choose
Opens instantly. Your files never leave this device — parsing runs 100% in your browser.
The address card, and the lines it leaves off
Once a file parses, the heading is the subject. An empty subject is painted as the literal marker "(no subject)". Under it, the card prints From, To, and Date, and it prints Cc only when that value is non-empty. Each of those lines is a button: activate it and the text is copied, with a short "Copied" confirmation. Date is not the raw header token. The parser keeps the original Date string, then, when that string parses as a timestamp, shows a locale rendering from the browser.
Two decoded fields do not reach the card. Reply-To and Bcc are read, encoded-words in them are undone, and they sit on the parse result. The screen never lists them. There is also no dump of every header line. Received hops, Message-ID, and authentication results remain in the source text. Anyone who needs that trail should open the file in a programmer's editor. Expecting this screen to enumerate it will be disappointed, and the page will not pretend otherwise.
Header text that arrives as RFC 2047 encoded-words — the =?charset?B?...?= and Q forms — is decoded before the card paints, and whitespace sitting between adjacent encoded-words is removed the way the RFC requires. A subject that looked like noise in a naive text dump should show as ordinary characters here, for the charsets the platform decoder accepts.
Two panes: the designed part and the plain alternative
The walker keeps the first text/html part that is not marked as an attachment, and the first text/plain part on the same rule. Later parts of a type already captured do not replace that pane. A second HTML part falls through onto the file list instead of becoming another tab of wording. Multipart/alternative is walked so both variants can be kept; other multipart kinds (mixed, related, and the rest) are walked the same way, part by part, up to a nesting depth of 20 so a hostile file cannot recurse without limit.
The HTML control appears only when an HTML part was stored. The plain control is always there. Default selection is the HTML pane when that part exists, otherwise the plain pane. HTML is drawn in an iframe whose sandbox blocks scripts, after a sanitizer removes script, form controls, svg, math, video, audio, and similar active tags, and strips style that would fetch a remote resource. A content-security policy is added to the document shell. Remote images are neutralized until a per-message control opts in. The notice next to that control says the picture host may learn an address, a browser description, and the open time. Remote CSS, fonts, media, and SVG stay out even after that opt-in.
The plain pane is a preformatted block of the decoded text part. When the sender shipped HTML only, that pane shows the fixed sentence that the email has no plain-text part, rather than inventing a text version by stripping tags.
Pictures packed inside the file, and the download rows
Marketing mail and invoices often refer to a logo with a cid: URL instead of a hosted address. Before the frame draws, those sources are matched to a listed part by Content-ID. Angle brackets around the id are stripped during the parse. The match accepts the token as written or after a URI decode, and only when the media type starts with image/. The replacement is a data URI, so the picture paints from bytes that were already in the file. A cid that does not match an image part is left unchanged.
Those image parts are still pushed onto the attachment array, so they remain listed after they appear in the wording. The list shows the filename, the media type, and a size, plus Download. Download builds a blob from the decoded bytes and saves it under that filename. Names prefer the Content-Disposition filename, then the Content-Type name parameter, then a generated attachment-N label. RFC 2231 filename* values, including continued segments, are joined in the parser before that label is chosen. An embedded message/rfc822 that is not itself flagged as an attachment is added to the same list as a downloadable .eml.
Non-image files are not previewed inside the wording. A PDF contract beside a cid logo shows up as its own row; only the logo is painted in the HTML, and only because the HTML pointed at it with cid. An image that is attached but never referenced that way stays a row, not an automatic illustration.
Transfer encodings the parser undoes first
Body bytes are recovered from Content-Transfer-Encoding before any charset work. Base64 is whitespace-tolerant. Quoted-printable drops soft line breaks and applies hex escapes. 7bit, 8bit, binary, and a missing label are taken as raw bytes. The charset parameter is then handed to the platform text decoder. An unknown or rejected label falls back to utf-8, and if that also fails, to a byte-preserving latin1 string. Nothing in this chain calls a server.
The repository checks this path with Node fixtures in scripts/test-emlParser.mjs: a 7bit plain message, a multipart message carrying one base64 attachment whose bytes round-trip, and a quoted-printable UTF-8 body whose subject and From use encoded-words. Cid rewriting lives in resolveCidImages inside the viewer component and is not one of those fixtures. RFC 2231 filenames are implemented in the header parser and are likewise outside that fixture list. Treat untested edges as untested.
Apple Mail .emlx is the same MIME text with a leading byte-count line and a trailing plist; the unwrap is described on the EMLX viewer. Outlook .msg is a different binary container and is opened by a separate parser; see the MSG viewer.
After the parts are on screen
Save as .eml writes the message bytes back to a file named from the subject, or a generic message name when the subject is empty. Export PDF builds a print document from the address lines plus the current body pane and calls the browser print function; it does not mint a PDF by itself. Save as HTML jumps to the converter page and does not carry the open file with it, so the file has to be chosen again there. None of those actions replace the download row on an individual part.
People who want the operating system to own the extension should read how to open an EML file. A longer note on the one-message print path is EML to PDF. The same box, framed for a machine where nothing can be installed, is the EML viewer online page, and the main EML viewer is the home of the tool.
FAQ
Which address lines does the card print?
From, To, and Subject, plus Cc when that line is non-empty, plus Date. Date is the Date header rendered in the browser locale when the timestamp parses, not the raw header token. Each populated line copies when activated. Reply-To and Bcc are decoded by the parser and then left off the card. There is no raw-header pane for Received hops, Message-ID, or authentication results.
Why are there separate HTML and plain-text controls?
A message often carries a designed HTML part and a text/plain alternative. The first HTML part and the first plain part become the two panes. The HTML control is shown only when an HTML part exists, and it is selected by default in that case. Otherwise the plain pane is selected. If the sender omitted text/plain, the plain pane says the email has no plain-text part. An extra HTML part after the first is listed as a file rather than a second pane.
How do pictures inside the wording get onto the page?
Image sources that use a cid: URL are matched to an attachment by Content-ID, including a decode of the token, and only when the media type starts with image/. The match is rewritten to a data URI before the sandboxed frame draws. Remote http and https images stay blocked until the per-message control opts in. That opt-in can contact the host named by the sender.
Can every enclosed file be saved, including ones meant only for display?
Yes. Every non-body part is listed with filename, media type, and size, and each row has Download. Inline images remain in that list after they are drawn in the body. Non-image files are not previewed inside the wording; they are download rows only. An embedded message/rfc822 is offered as a downloadable .eml. Names come from the disposition filename, else the Content-Type name, else a generated attachment-N label, with RFC 2231 filename* segments joined when present.
What happens to base64 bodies and encoded subject lines?
Content-Transfer-Encoding is undone first: base64, quoted-printable (soft breaks included), or raw 7bit, 8bit, or binary. The charset label is then applied, falling back to utf-8 and then a byte-preserving latin1 string if the label is unknown. Header encoded-words (RFC 2047, both B and Q) are decoded before the card paints. The Node fixtures cover a 7bit plain message, one base64 attachment, and a quoted-printable body with encoded-words. They do not cover cid rewriting or RFC 2231 names; those paths are still in the parser and the viewer.
Need to convert many emails at once? Drop 50 .eml files, get 50 PDFs in a ZIP — one click, still 100% in your browser. Try the first 3 free.
- $19.99 — 24-hour batch pass: unlimited files and unlimited re-runs for 24 hours after your first batch finishes.
- $39.99 — lifetime: unlimited batches, forever.
Both one-time. No subscription.