Runs in your browser · nothing is uploaded
JavaScript QR Code Generator
Most “QR generators” only ever take a URL. This one builds the payload the way a scanner expects it — Wi-Fi credentials, a vCard, an email with a pre-filled body, an SMS — and shows you the exact string going into the symbol, escaping visible, next to the byte count, the version and the error-correction level that decide whether it will scan.
Build the payload
Pick what the code should carry. The string below is exactly what gets encoded — including the backslashes that decide whether a phone accepts it.
A URL QR code stores the link itself — there is no redirect service, so the code never expires as long as you keep the page.
Payload
This is the whole content of the symbol.
https://jsgenerator.com- Bytes
- 23
- Version
- —
- Modules
- —
- Level
- M
Preview and files
Preview renders at 280 px ≈ — px per module—. The PNG exports at 512 px; for print use the SVG, which stays sharp at any size. Need a branded code with a logo in the middle? That is a separate tool.
React / browser — canvas render with this exact payload
const payload = "https://jsgenerator.com";
import QRCode from "qrcode";
const canvas = document.querySelector("canvas");
await QRCode.toCanvas(canvas, payload, {
width: 512, // pixels — scale up for print, see the size table
margin: 2, // quiet zone in modules; the spec minimum is 4
errorCorrectionLevel: "H",
});Same payload, emitted as an SVG string
const payload = "https://jsgenerator.com";
import QRCode from "qrcode";
// An SVG string scales to any print size and stays one file in your repo.
const svg = await QRCode.toString(payload, {
type: "svg",
margin: 4,
errorCorrectionLevel: "H",
color: { dark: "#111827", light: "#00000000" }, // transparent background
});What breaks url codes in the wild
- Every URL QR code is static: the encoded string is the final link, so the code cannot be re-targeted later. Dynamic QR services sell you a redirect URL precisely because they can rewrite it server-side.
- Shorten the path if the code must print small. Byte mode capacity at version 10 / level M is 213 characters, but the symbol gets denser with every extra byte and dense symbols are what phones fail on.
- Test the exact printed size, not the on-screen preview. Camera focus distance changes everything below about 20 mm of module area.
What each payload type actually stores
A QR code holds one string. The “type” is a convention about how that string is written, and the scanner decides what to offer you — join a network, save a contact, compose a message — by reading its prefix.
| Type | String shape | The part people get wrong |
|---|---|---|
| URL | https://<host>/<path> | Every URL QR code is static: the encoded string is the final link, so the code cannot be re-targeted later. Dynamic QR services sell you a redirect URL precisely because they can rewrite it server-side. |
| Wi-Fi | WIFI:T:<sec>;S:<ssid>;P:<pass>;; | The four characters `\` `;` `,` `:` must be backslash-escaped inside SSID and password, and the backslash has to be escaped first. This is the single most common cause of a Wi-Fi code that scans but reports the wrong password. |
| vCard | BEGIN:VCARD / VERSION:3.0 / ... / END:VCARD (CRLF between lines) | Lines must end with CRLF (`\r\n`). Cards joined with LF only are accepted by some readers and silently dropped by others, most often on iOS Contacts. |
mailto:<address>?subject=...&body=... | The payload is a `mailto:` URL, so subject and body are percent-encoded — spaces become `%20` and the payload grows fast. Roughly 1 in 6 of the printed size is text, the rest is escaping overhead. | |
| SMS | SMSTO:<number>:<message> | `SMSTO:<number>:<text>` is the form iOS and Android readers both parse. `sms:<number>?body=` is iOS-only and `smsto:` is Android-flavoured; picking one of those two costs you the other platform. |
| Plain text | <utf-8 text> | Byte mode is what the `qrcode` library uses unless the whole input is numeric or alphanumeric, so non-ASCII text costs one byte per character below U+0080 and two or three above it. |
Wi-Fi QR codes: the escaping rule that decides whether it works
This is the single most-asked question in this family of formats, and it fails silently: a wrongly escaped payload still produces a perfectly normal-looking picture. The scanner reads the wrong password and reports a bad key.
WIFI:T:WPA;S:Office\;Guest;P:p\,a\:ss;;Characters to escape
\ | \\ | must be replaced first, or you double-escape |
; | \; | field separator |
, | \, | list separator |
: | \: | key/value separator |
" | \" | quote delimiter |
Security tokens
WPAcovers WPA2- and WPA3-Personal. Do not inventWPA3; readers only implement the documented set.WEPfor legacy open-legacy networks.nopass— and drop theP:field entirely.EAPfor 802.1X, withI:for the identity. Support is uneven; for an event, use a separate WPA2-PSK guest network.H:trueonly when the SSID is not broadcast.
Remember what you are publishing: a printed Wi-Fi code is the plain-text password, readable by anyone who photographs the sign. Encode a guest network you can rotate, never the one your laptops live on.
vCard QR codes: CRLF, and both name fields
A vCard is a text file with a small grammar. Two details separate a card that imports from one that opens an empty contact: the line terminator, and writing the name twice.
The properties this tool writes
N— family;given;additional;prefix;suffix — used for sortingFN— the display name; importers show this oneORG / TITLE— company and job titleTEL;TYPE=CELL— mobile; add TYPE=WORK,VOICE for the desk numberEMAIL;TYPE=INTERNET,PREF— the address to save as defaultURL— website, with the scheme includedADR;TYPE=WORK— 7 ordered components; empty ones stay as empty segmentsNOTE— free text, shown in the contact's note field
Rules that are not optional
- Lines end with CRLF. LF-only cards are accepted by some readers and dropped by others — usually iOS Contacts.
- The card must start
BEGIN:VCARD, thenVERSION:3.0, and end withEND:VCARD. - Escape
\,;and,inside values; write a line break as the two characters\n. - Stay on 3.0. Mixing in 4.0-only properties is how people end up with a code that saves nothing.
- For a badge where space is tight, MeCard is shorter and has no CRLF requirement — but it cannot carry the fields above.
Error correction, version, and the capacity table
Error correction adds redundant codewords so a dirty, faded or partly covered symbol still reads. It is bought with capacity, which is why a logo and a long payload fight each other. These are byte-mode numbers — UTF-8 characters of text, the mode real payloads use:
| Version | L (7%) | M (15%) | Q (25%) | H (30%) | Typical use |
|---|---|---|---|---|---|
| 121×21 | 17 | 14 | 11 | 7 | A short link, a tel: string, a Wi-Fi code with a simple password. |
| 225×25 | 32 | 26 | 20 | 14 | The usual landing zone for a URL with a couple of query parameters. |
| 329×29 | 53 | 42 | 32 | 24 | Still prints legibly on a 20 mm label. |
| 433×33 | 78 | 62 | 46 | 34 | A vCard with name, one phone number and an email. |
| 537×37 | 106 | 84 | 60 | 44 | Wi-Fi with a long WPA3 passphrase, or a MeCard. |
| 1057×57 | 271 | 213 | 151 | 119 | A full vCard with address and note. Density starts to matter on paper. |
| 2097×97 | 858 | 666 | 482 | 382 | Bulk text, a vCard with a logo. Needs a large print area. |
| 40177×177 | 2953 | 2331 | 1663 | 1273 | The format maximum. 177 modules a side — do not try to scan this off a badge. |
Every cell above is checked against the encoder by this site's build guard, which asks the library where each version stops accepting bytes. That is also how the 4,296-byte figure you will find quoted on generator sites got corrected here: 4,296 is version 40 level L in alphanumeric mode — uppercase ASCII and digits only. In byte mode the ceiling is 2,953.
The whole implementation, in one dependency
Everything on this page runs on the qrcode package — the same one this tool uses. No service, no API key, no upload.
Install
npm install qrcode
# types ship separately:
npm install -D @types/qrcodeNode.js — write a PNG file
import QRCode from "qrcode";
import { writeFile } from "node:fs/promises";
const payload = "WIFI:T:WPA;S:Office;P:pass1234;;";
const buffer = await QRCode.toBuffer(payload, { width: 1024, margin: 4, errorCorrectionLevel: "M" });
await writeFile("wifi-qr.png", buffer);React — a component that re-renders on change
"use client";
import { useEffect, useRef } from "react";
import QRCode from "qrcode";
export function Qr({ payload, size = 256, level = "M" }) {
const ref = useRef(null);
useEffect(() => {
if (!ref.current || !payload) return;
QRCode.toCanvas(ref.current, payload, {
width: size,
margin: 2,
errorCorrectionLevel: level,
});
}, [payload, size, level]);
return <canvas ref={ref} aria-label="QR code" />;
}Two things to know before you ship that component. toCanvas needs a real canvas element, so it cannot run during server-side rendering — keep it in a client component. And if you render into the same canvas while the payload is empty, you get a blank box instead of an error, which is why the tool above refuses to draw until there is something to encode.
Printing it without it failing on the day
Size the module, not the image
Keep the printed module edge at 0.7 mm or more. A 33-module code needs about 29 mm including its quiet zone; a 73-module vCard needs about 57 mm. Exporting a 3,000-pixel PNG does not help if it is printed at 20 mm.
Contrast beats colour
Aim for a contrast ratio of at least 4.5:1 between the modules and the background. Dark-on-light is the safe direction; light-on-dark inverts the finder patterns and some readers refuse it, however correct the data is.
Test the real artefact
Scan the printed proof, at the distance people will actually stand, on one iPhone and one Android. This is the only check that counts, and it is the reason every tool on this page says what it says out loud.
Prefer SVG for anything printed: it is resolution-independent, it stays one file in a design document, and the logo overlay on the companion tool is placed in module units, so it keeps its proportions at 300 dpi and at 3,000.
Frequently asked questions
How do I generate a QR code in JavaScript?
Install the qrcode package, call QRCode.toCanvas(canvas, payload, options) in a browser or QRCode.toString(payload, { type: "svg" }) anywhere, and pass the string you want encoded. Nothing about that flow needs a server: the symbol is computed from the text alone.
How do I make a Wi-Fi QR code in JavaScript?
Build the payload as WIFI:T:<security>;S:<ssid>;P:<password>;; and escape every backslash, semicolon, comma, colon and double quote inside the SSID and password with a preceding backslash — the backslash has to be escaped first. Feed that string to the same QR encoder you use for URLs. Omit the P: field entirely for an open network, and add H:true when the SSID is hidden.
Can I use this in React or Next.js?
Yes. Put the encoder inside a client component, re-run it in an effect whenever the payload changes, and render into a canvas ref. Because generation is synchronous and local, there is no loading state to manage and nothing to debounce beyond keeping the render off the first paint. The snippets on this page are written for React 19 and Next.js App Router.
What is the most data a QR code can hold?
In byte mode — the mode any real payload lands in — the maximum is 2,953 bytes at version 40 with error correction level L. You will often see 4,296 quoted; that is version 40 level L in alphanumeric mode, which only counts for uppercase ASCII and digits. Practical payloads here stay under 300 bytes.
Does a QR code expire?
A static QR code never expires, because it stores the final content and nothing else. What expires is the destination: a shortened or campaign URL that you later delete will 404 even though the symbol still scans perfectly. Codes that can be re-targeted are a paid service, not a property of the format — they encode a redirect URL that someone else controls.
Why does my QR code scan on one phone and not another?
Almost always density or contrast, not the data. Keep the printed module edge at or above 0.7 mm, leave a quiet zone of at least four modules, keep the contrast ratio above 4.5:1, and avoid inverted (light-on-dark) symbols unless you have tested them on the specific readers you expect. A code that works on the developer's Pixel and not on an older iPhone is usually one of those four.
Need a logo, a print sheet, or 3,000 pixels?
The with-logo tool composes a centred mark over the same symbols, reports how much of the code it covers, and exports an A4 label sheet from a CSV — all in the browser, with no upload step.
Open the with-logo tool