Email QR Code Generator
One scan opens the visitor's own mail app with your address — and optionally a subject and message — already filled in. All they do is write and press send. Generated entirely in your browser.
Print checklist
- Keep the white margin around the code — it is the quiet zone scanners need, not empty space to crop.
- Size for the scan distance: at least 2 × 2 cm for arm's-length scanning, and roughly one tenth of the farthest scan distance for posters.
- Scan a test print with a real phone before making copies.
How mailto: codes work
The code carries a standard mailto: link — the same format an email link on a webpage
uses (defined in RFC 6068). Scanning it opens the phone's default mail app with a new draft:
recipient from the link, plus subject and body if you provided them. Special characters in the subject
and body are percent-encoded automatically, so "Q&A?" arrives as "Q&A?" and not as a broken
link.
Because the draft opens in the visitor's own mail account, replies go through their address — you receive a normal email you can answer, which is often exactly the low-friction contact channel a poster or product needs.
The payload has three parts, and the builder assembles them in a fixed order. First the
scheme, mailto:, which is what marks the string as an email link rather than a
web address or plain text. Then the address, trimmed of surrounding whitespace and otherwise
as you typed it, after a validation pass and one narrow encoding rule. Then — only when you
filled in a subject or a message — a
? followed by the header fields, subject=… and body=…,
joined by &. An address on its own produces a bare mailto: link with
no question mark at all.
Two different encoding rules apply, because the two halves of the string answer to two
different parts of the specification. The address is an addr-spec, and RFC 6068
§2 requires %, &, ; and
= to be percent-encoded when they appear inside it. The builder does this in a single
pass, so a literal % is encoded exactly once and never double-encoded, and every other
character in the address — dots, plus signs, hyphens, digits — is left exactly as entered. The subject
and message are header values, and they are encoded strictly to RFC 3986: every
character outside the unreserved set (letters, digits, -,
., _ and ~) becomes a percent sequence. That is
stricter than a browser's encodeURIComponent, which leaves the sub-delimiters
! ' ( ) * bare even though they are reserved characters; the builder encodes those
too, so each header value contains only unreserved and percent-encoded characters. A space becomes
%20, and a character outside ASCII — an em dash, an accented letter —
becomes the percent-encoded bytes of its UTF-8 form.
These are conventions, not inventions of this site, and each is named with its source:
RFC 6068 for the mailto: URI and the ZXing "Barcode Contents" wiki for how the
URI is carried in a barcode, both verified against the live sources on 2026-07-20; the percent-encoding
follows RFC 3986, which the builder's own comment cites. The builder is held to them by automated
tests against independently constructed reference strings, hostile inputs included, and by the round-trip
suite that renders each payload type to an image and decodes it back byte for byte. For how the
same scheme-plus-encoding idea plays out across
tel:, sms: and WhatsApp links, read the guide
URI schemes: mailto, tel, sms and WhatsApp; the rest of the series is at /guides/.
Worked example
A guest-feedback code for a hotel room encodes as:
mailto:feedback@example.com?subject=Feedback%20%E2%80%94%20Room%2012&body=What%20went%20well%20%2F%20what%20could%20be%20better%3A
Note the encoding: spaces become %20, the em-dash is percent-encoded, and the
subject identifies the room so feedback arrives pre-sorted.
Reading the worked example
Take the example apart from left to right. The scheme and the address come first and are
untouched: feedback@example.com contains none of the four characters the addr-spec rule
encodes. (Every address on this page is a fictional example.com one; none is a real
mailbox.) Then ? opens the header fields. Here is each field as typed and as it appears
in the payload:
| Field | As typed | In the payload |
|---|---|---|
| Subject | Feedback — Room 12 | Feedback%20%E2%80%94%20Room%2012 |
| Message | What went well / what could be better: | What%20went%20well%20%2F%20what%20could%20be%20better%3A |
In the subject, each space became %20, the em dash became
%E2%80%94 — the percent-encoded bytes of its UTF-8 form — and the letters and digits
passed through untouched because they are in the unreserved set. In the message, the slash became
%2F and the colon %3A; neither is unreserved, so
both are encoded. The one bare & in the payload belongs to neither field: it is
the delimiter between subject= and body=, the only place an
unencoded ampersand can appear. An ampersand typed inside the subject becomes
%26, which is exactly what keeps it from being read as a delimiter.
The symbol facts — version, module count and error-correction level — are the second layer,
the one the reference encoder owns. Version 8 is the version the encoder chose to
hold the payload at the requested error-correction level, and the side length follows from it: a
symbol is 17 + 4 × version modules across, so 17 + 4 × 8 = 49. Error correction M is the level actually encoded: this page asks for M, the generator's
default, and the reference encoder never goes below the requested level but may go above it when
a higher level fits in the same version. Add the 4-module quiet zone the
renderer draws on every side and the image you download is 57 modules wide.
Three more payloads, built by the same engine
Every payload below was produced at build time by calling the builder the form uses, on fictional inputs, and then handed to the same encoder for its symbol. Nothing here was typed by hand, and each example isolates one rule.
Address hello@example.com, no subject, no message:
mailto:hello@example.com
Nothing is appended — no ?, no header fields. Version 2, 25×25 modules, error correction M: the smallest
symbol the generator makes for this address.
Set it beside the worked example and the cost of the headers is visible: the subject and message raised the version from 2 to 8, which is 24 more modules per side.
Address tickets@example.com, subject Q&A: 50% off? (Tuesday) — don't miss it!, no
message:
mailto:tickets@example.com?subject=Q%26A%3A%2050%25%20off%3F%20%28Tuesday%29%20%E2%80%94%20don%27t%20miss%20it%21
Version 7, 45×45 modules, error correction M. A subject chosen because it packs in many of the characters that cause trouble.
Read the subject against the payload. & became %26
and ? became %3F; both would otherwise be read as URI
structure. % became %25, so the 50% survives instead
of starting a bogus escape sequence. The parentheses, the apostrophe and the exclamation mark became
%28, %29,
%27 and %21. Those are the ones the strict rule
adds; a browser's encodeURIComponent leaves them bare:
| Character | encodeURIComponent | This builder |
|---|---|---|
! | ! | %21 |
' | ' | %27 |
( | ( | %28 |
) | ) | %29 |
* | * | %2A |
The builder applies the same strict rule to mailto: and sms: header values,
so every character inside a header value is either unreserved or percent-encoded; the scheme, the
@ and the ?, = and & separators around the values stay bare.
Address support+kiosk@example.com, subject Kiosk fault, and
a two-line message — Kiosk number: on the first line, What happened:
on the second:
mailto:support+kiosk@example.com?subject=Kiosk%20fault&body=Kiosk%20number%3A%0AWhat%20happened%3A
Version 6, 41×41 modules, error correction M. Two rules at once: the plus sign in the address and the line break in the message.
The + in the address is untouched, because the addr-spec pass encodes only
%, &, ; and =; a plus-addressed
mailbox goes into the code as written. In the message, the line break became
%0A and each colon %3A, and the
& between subject= and body= is, again, structure rather
than text. The payload carries the line break as %0A; the engine's job
ends there.
One rule remains that only the address can trigger. An address containing one of the four
addr-spec characters is unusual, but the validator accepts it — it checks shape, not mailbox
conventions — and the rule exists for it: news&events@example.com builds as
mailto:news%26events@example.com, with the ampersand encoded inside the address in the single pass
that guarantees it is encoded exactly once. The address-only payload shows the other side of
the same rule: when none of the four characters is present, nothing in the address changes at
all.
Side by side, every symbol on this page:
| Example | Characters | Version | Modules | Error correction |
|---|---|---|---|---|
| Worked example: address, subject and message | 130 | 8 | 49×49 | M |
| Address only | 24 | 2 | 25×25 | M |
| Reserved-character subject | 113 | 7 | 45×45 | M |
| Plus-address and two-line message | 98 | 6 | 41×41 | M |
How to read the symbol facts and the print-size readout
- Version and modules. The encoder chooses the version that holds the payload at the requested error-correction level, and the side length is 17 + 4 × version, so each step up adds four modules per side. More characters mean a higher version and, at any given print size, more and smaller modules. If the content will not fit even at the highest version the encoder supports, the generator refuses rather than guessing, with the message "This content is too long for a QR code. Shorten it or lower the error correction level."
- Error correction. Four levels, L, M, Q and H. The generator defaults to M and lets you choose any of the four when there is no logo; a logo overlay removes the choice, forcing H, and is capped at 16% of the symbol area — a cap set by decode testing in the round-trip suite, not derived from the theoretical ~30% EC-H recovery ceiling. The level reported is the one actually encoded, and it can be higher than the one requested: the reference encoder raises the level whenever doing so does not push the symbol into a larger version. The table below shows it happening.
- Quiet zone. The 4-module light border on every side is part of the symbol as ISO/IEC 18004 defines it. The renderer draws it into every SVG and PNG and offers no way to turn it off, so a downloaded image is 8 modules wider than the module count alone suggests.
- Print size. The PNG download comes in 3 preset widths, and for each the download panel reports the physical size at 300 DPI: inches are pixels divided by 300, and millimetres are inches multiplied by 25.4. The last two columns below divide that width among the modules — quiet zone included — for the shortest and longest payloads on this page.
| PNG preset | Print size at 300 DPI | Module width, address only (33 across) | Module width, worked example (57 across) |
|---|---|---|---|
| 512 px | 1.71 in × 1.71 in (43.3 mm × 43.3 mm) at 300 DPI | 1.31 mm | 0.76 mm |
| 1024 px | 3.41 in × 3.41 in (86.7 mm × 86.7 mm) at 300 DPI | 2.63 mm | 1.52 mm |
| 2048 px | 6.83 in × 6.83 in (173.4 mm × 173.4 mm) at 300 DPI | 5.25 mm | 3.04 mm |
The module widths are arithmetic on the readout, not a rule: they tell you how wide one square of this particular symbol is at each preset, and therefore what adding a longer message does to it. What they cannot tell you is whether a given print will scan. Error correction helps a damaged code read, but print size and contrast still matter, and the engine's position is the same for every type: treat a freshly generated code as untested until a real device has scanned it.
The same two payloads at every error-correction level
Requested level on the left; what the encoder produced for the bare address and for the full worked example on the right. Compare the two halves row by row.
| Requested | Address only: version | Modules | Encoded | Worked example: version | Modules | Encoded |
|---|---|---|---|---|---|---|
| L | 2 | 25×25 | M | 6 | 41×41 | L |
| M | 2 | 25×25 | M | 8 | 49×49 | M |
| Q | 3 | 29×29 | H | 9 | 53×53 | Q |
| H | 3 | 29×29 | H | 11 | 61×61 | H |
Where the encoded level is higher than the requested one, that is the boost: the payload had capacity to spare in its version, and the encoder spent it on recovery rather than leaving it empty, at no cost in size. Where the two match, the next level up would not have fit without a larger symbol — or H, the top level, was already requested. Either way the level printed under a code is a fact about what was encoded, not a setting that was ignored.
Where email codes beat contact forms
- Feedback at the point of experience: a code in the hotel room, on the table, or on the invoice catches people while the impression is fresh — no form to build or host.
- Support with context baked in: pre-fill the subject with the product or location ("Issue — Ticket machine, Platform 2") and every message arrives pre-triaged.
- Print and packaging: a support address on the box that nobody has to type (and therefore nobody mistypes).
Prefill etiquette mirrors WhatsApp codes: seed context, keep it short, and leave the actual message to the sender. A body that is one prompt line ("What went well / what could be better:") gets more and better replies than an empty draft — or a pushy prewritten one.
What the generator cannot verify
The engine validates format, not truth, and on this page the line falls in a specific place.
The address check is a shape test: no spaces, exactly one @, and a dot after it
with characters on both sides. Anything else is refused with "This is not a valid email address.", and an
empty field with "Enter the recipient email address." That is everything the builder can know about an
address. It cannot know whether the mailbox exists, whether anyone reads it, or whether you
typed the one you meant:
mailto:feedbcak@example.com is a perfectly well-formed payload and, if the real address is
feedback@example.com, a perfectly useless code. The subject and message are not
checked at all, only encoded.
Shape checking has one consequence worth seeing once. The field takes an address, not a link:
paste mailto:hello@example.com into it and the shape test passes — no spaces, one
@, a dot after it — so the builder prepends the scheme a second time and encodes
mailto:mailto:hello@example.com. The generator's "What the code contains" panel shows the payload
exactly as it will be encoded, which is where a doubled scheme, a typo or a missing subject is
caught before anything is printed.
Then there is everything that follows from the code being static. The address and headers live
in the image; nothing routes through this site, so there is no redirect that can expire, break
or be switched off, and no third party — including us — can hold the code hostage. The same
fact means the code cannot be edited once printed, so a changed address is a new code, and
scans cannot be counted. The encoding runs in your browser, and the examples on this page were
computed at build time by the same engine, so if the encoding ever changed the copy would
change with it or the build would fail. The sibling schemes share the boundary exactly — a tel: code cannot know a number is reachable any more than this one can know a mailbox is read — and
the guide
URI schemes: mailto, tel, sms and WhatsApp
sets the four side by side.
Common misreadings
- Cropping the quiet zone. The light border is not padding. It is the 4-module margin the standard requires, rendered into every download and impossible to disable here, because cropping it is the most common self-inflicted scan failure. Printing the code flush against artwork removes it just as surely as cropping the file does.
- Inverting the colours. Light modules on a dark background are rejected by many scanners, and the style panel warns separately for that case. It also evaluates your colour pair with the WCAG relative-luminance contrast formula and warns below 4.5:1. Both warnings are computed from the colours you chose, not guessed.
- "Scanning sends the email." It does not. The code opens a draft; the visitor writes and presses send, or closes it. The code cannot send anything on its own.
- "A wrong address would fail to encode." It encodes perfectly, as the typo above shows; the only check is shape. Scan a test code with a real device and confirm the draft is addressed where you meant before printing a run.
- "A longer message makes a better code." It makes a bigger payload, a higher version and smaller modules at the same print size — version 2 for the bare address against version 8 for the worked example.
- Typing the scheme yourself. The field takes the address; the builder adds
mailto:. Typed in by hand, it is doubled, as shown above.
Email QR code FAQ
What if the person has no mail app configured?
The phone will offer whatever handles mailto: — on iOS and Android that is virtually always at least the built-in mail app or Gmail. On a device with truly no mail client, the scan shows the address so it can be copied.
Can I send to multiple recipients or add CC?
This generator keeps to a single recipient deliberately: multi-recipient mailto: handling is inconsistent across mail apps, and a shared inbox address is the reliable way to reach a team.
Will spam bots harvest my address from the code?
A printed code is far less harvestable than an address written on a webpage — but the address is readable by anyone who scans, so use a monitored role address (feedback@…) rather than a personal one where that matters.
Why does the payload show %20 instead of a space?
Because the subject and message travel inside a URI, where a space has no legal place. The builder percent-encodes every character outside the RFC 3986 unreserved set — letters, digits and - . _ ~ — so a space becomes %20 and an em dash becomes %E2%80%94, the percent-encoded bytes of its UTF-8 form. The text underneath is unchanged: "Q&A?" still arrives as "Q&A?", as the section above shows character by character.
Does scanning the code send the email?
No. The code carries only a mailto: link. Scanning it opens a draft in the visitor's own mail app, and nothing is sent until they press send — or nothing at all, if they close it. The code cannot send anything on its own: the encoding runs in your browser, and the link lives in the image with no redirect through our servers.
Can I change the address, subject or message after printing?
No. Every code here is static: the text lives in the image, with no redirect through our servers, so nothing can expire or be switched off — and nothing can be edited either. A new address or subject is a new code, and scans cannot be counted for the same reason. The trade-off is stated on the methodology page rather than sold around.
Why does the code get denser when I add a message?
A longer payload needs a higher QR version, and the symbol grows by four modules per side with each version (17 + 4 × version). The bare address mailto:hello@example.com is version 2, 25×25 modules; the worked example with a subject and message is version 8, 49×49. At the same print size those modules are smaller, which is what the module-width table above measures — and why a long message is the case where the readout and a real test scan matter most.
For instant-message channels, see the SMS code and the WhatsApp code; to hand over full contact details, use the vCard generator.