Lean QR is a lightweight yet fully-featured library for generating QR Codes. Also available as an online tool.
Quickstart
Choose your platform:
NodeJS
Install the dependency:
npm install --save lean-qr
Display QR Code in terminal output:
import { generate } from 'lean-qr'; const code = generate('LEAN-QR LIBRARY'); process.stdout.write(code.toString({ on: '\u001B[7m \u001B[0m', // ANSI escape: inverted }));
Generate SVG source for serving from a webserver:
import { generate } from 'lean-qr'; import { toSvgSource } from 'lean-qr/extras/svg'; const code = generate('LEAN-QR LIBRARY'); const svg = toSvgSource(code, { // set to false if you want to embed the QR Code inside a HTML document xmlDeclaration: true, });
Generate PNG for serving from a webserver:
import { generate } from 'lean-qr'; import { toPngBuffer } from 'lean-qr/extras/node_export'; const code = generate('LEAN-QR LIBRARY'); const png = toPngBuffer(code, { scale: 8 });
Note: SVG is the recommended file export format as it will automatically scale without blurring, but you
can also render PNGs using the CSS: image-rendering: pixelated
.
Browser Native
Install the dependency:
npm install --save lean-qr
The recommended in-browser approach is to render directly to a canvas
element, and size it
using CSS:
<canvas id="my-canvas" /> <script type="module"> import { generate } from 'lean-qr'; const code = generate('LEAN-QR LIBRARY'); code.toCanvas(document.getElementById('my-canvas')); </script> <style> #my-canvas { width: 100%; image-rendering: pixelated; } </style>
Providing a download link
If you want to offer users a way to download the QR code as an image file, you can use a data URL:
<a href="#" download="qr.png" id="download-link">Download</a> <script type="module"> import { generate } from 'lean-qr'; const code = generate('LEAN-QR LIBRARY'); const dataUrl = code.toDataURL({ scale: 10 }); document.getElementById('download-link') .setAttribute('href', dataUrl); </script>
Note that providing an SVG download is preferable for letting users scale the image without it blurring, but this may not be supported by all image editors. The default format is PNG.
React
Install the dependency:
npm install --save lean-qr
A convenience wrapper component is provided for React:
import { generate } from 'lean-qr'; import { makeAsyncComponent } from 'lean-qr/extras/react'; import * as React from 'react'; // creates a react component (do this at the top-level) const QR = makeAsyncComponent(React, generate); // example usage: const MyComponent = () => ( <section> Scan this QR Code! <QR content="LEAN-QR LIBRARY" className="qr-code" /> </section> );
You should apply some styling to the QR Code to set its size:
.qr-code { width: 300px; }
Note that this uses canvas
rendering, which is optimal on the client-side, but will not work
if you want to perform server-side rendering (the QR Code will not be visible until client-side hydration
has completed). If you need server-side rendering, use
makeSyncComponent
instead:
import { generate } from 'lean-qr'; import { toSvgDataURL } from 'lean-qr/extras/svg'; import { makeSyncComponent } from 'lean-qr/extras/react'; import * as React from 'react'; const QR = makeSyncComponent(React, generate, toSvgDataURL); const MyComponent = () => ( <section> Scan this QR Code! <QR content="LEAN-QR LIBRARY" className="qr-code" /> </section> );
Preact
Install the dependency:
npm install --save lean-qr
A convenience wrapper component is provided for Preact:
import { generate } from 'lean-qr'; import { makeAsyncComponent } from 'lean-qr/extras/react'; import { createElement } from 'preact'; import * as hooks from 'preact/hooks'; // creates a preact component (do this at the top-level) const QR = makeAsyncComponent({ createElement, ...hooks }, generate); // example usage: const MyComponent = () => ( <section> Scan this QR Code! <QR content="LEAN-QR LIBRARY" class="qr-code" /> </section> );
You should apply some styling to the QR Code to set its size:
.qr-code { width: 300px; }
If you want to reduce the build size further, you can provide just the useRef
and
useEffect
hooks rather than all hooks to enable tree shaking optimisations during the build.
Note that the set of required hooks may change in future versions.
Shell (CLI)
Though not recommended for production use, it is possible to use the built-in commandline interface to generate QR Codes from shell scripts:
npx lean-qr 'MY MESSAGE HERE'
By default this will print the QR Code to stdout
using ANSI escape sequences, so it is
recommended that you set a maximum version to ensure the QR Code does not exceed the width of the screen
(the output width will be (17 + version * 4) * 2
characters):
npx lean-qr --max-version 5 'MY MESSAGE HERE'
You can also customise the output format (e.g. to generate a file):
npx lean-qr --format svg '漢字' > my-qr-code.svg
For full documentation, run:
npx lean-qr --help
API Reference
-
generate(content[, options])
- Bitmap2D
- extras/svg
- extras/node_export
- extras/react
- extras/errors
- Error Reference
- Custom Modes
generate(content[, options])
import { generate, correction, mode } from 'lean-qr'; const code = generate('LEAN-QR LIBRARY', { minVersion: 1, maxVersion: 40, minCorrectionLevel: correction.L, maxCorrectionLevel: correction.H, mask: null, trailer: 0xEC11, modes: [ mode.numeric, mode.alphaNumeric, mode.ascii, mode.iso8859_1, mode.shift_jis, mode.utf8, ], });
The options shown are the defaults. See the following sections for details on their meaning.
Returns a Bitmap2D
containing the generated QR Code, or
throws an error if it is not possible to generate a QR Code with the requested content
/ options.
Versions (minVersion
/ maxVersion
)
QR Code versions refer to the size of the output (the size will be 17 + version * 4
, plus
outer padding).
By default, all versions can be used. To restrict this, you can specify a minimum and/or maximum version:
const code = generate('LEAN-QR LIBRARY', { minVersion: 10, maxVersion: 20, });
Versions must be integers in the range 1–40 (inclusive).
If there is too much data for the maxVersion
size, an
error will be thrown.
Correction Levels (minCorrectionLevel
/ maxCorrectionLevel
)
QR Code correction levels control the amount of damage the QR Code can sustain before it becomes unreadable.
You can specify minimum and maximum correction levels:
const code = generate('LEAN-QR LIBRARY', { minCorrectionLevel: correction.M, maxCorrectionLevel: correction.Q, });
correction level | error tolerance | data overhead |
---|---|---|
correction.L |
~7.5% | ~25% |
correction.M |
~15.0% | ~60% |
correction.Q |
~22.5% | ~120% |
correction.H |
~30.0% | ~190% |
generate
will pick the smallest
version (size) which supports the minCorrectionLevel
, then within this
version will use the highest possible correction level up to maxCorrectionLevel
.
Generally it does not make sense to customise the maxCorrectionLevel
unless you are
targetting a reader which does not support a particular correction level, or you are forcing a particular
level for stylistic purposes.
Masks (mask
)
QR Code masks are patterns which are applied to the resulting QR Code in an effort to maximise readability for scanners (e.g. by avoiding data sections that look like a locator pattern, or large areas of the same colour). To achieve this, ISO 18004 requires that the mask is chosen according to a specific algorithm (and this is the default behaviour), but it is also possible to explicitly specify a non-optimal mask:
const code = generate('LEAN-QR LIBRARY', { mask: 5, });
Valid masks are integers in the range 0–7 (inclusive).
Note that this may make your QR Code more difficult to scan, and should generally be left as the default
(null
).
Modes
The content you are encoding into a QR Code can be represented using a variety of "modes" (character encodings). These can be combined to achieve some amount of compression, allowing more content to fit in a smaller space.
By default, the optimal encoding mode is chosen to minimise the resulting QR Code size (this includes switching modes part way through a message if it reduces the size).
If you want to change the modes considered during this optimisation (perhaps if you are targeting a reader which does not support certain modes, or if you want to support a non-standard mode), you can specify a list of options:
const code = generate('LEAN-QR LIBRARY', { modes: [ mode.numeric, mode.alphaNumeric, mode.ascii, mode.iso8859_1, // mode.shift_jis, - example: exclude Shift-JIS mode.utf8, myCustomMode, // see Custom Modes below ], });
Or for full control, you can define the modes before passing the content in, avoiding the automatic optimisation entirely:
import { generate, mode } from 'lean-qr'; const code = generate(mode.alphaNumeric('LEAN-QR LIBRARY'));
Note that if you specify a mode explicitly, it is your responsibility to ensure the content you are encoding conforms to the accepted character set. If you provide mismatched content, the resulting QR Code will likely be malformed. The available modes and their supported character sets are:
mode | bits / char | charset |
---|---|---|
mode.numeric |
10 / 3 | 0-9 |
mode.alphaNumeric |
11 / 2 | 0-9A-Z $%*+-./: |
mode.ascii |
8 / 1 | 7-bit ASCII |
mode.iso8859_1 |
8 / 1 | ISO-8859-1 |
mode.shift_jis |
13 / 1 | Double-byte Shift-JIS characters |
mode.utf8 |
varies | Unicode |
There are also some meta-modes:
multi(...modes)
mode.multi
enables switching modes during a message:
const code = generate(mode.multi( mode.iso8859_1('https://example.com/'), mode.numeric('123456789012345678901234567890'), mode.alphaNumeric('/LOOKUP'), ));
eci(value)
/ bytes(data)
mode.eci
lets you switch the Extended Channel Interpretation of the message (Wikipedia includes a list of recognised values). After setting this, subsequent mode.bytes
will be interpreted in the specified
character set.
const code = generate(mode.multi( mode.eci(24), // Arabic (Windows-1256) mode.bytes([0xD3]), // Shin character ));
mode.eci
will avoid outputting additional switches if the ECI already matches the requested
value.
Note that mode.iso8859_1
sets ECI 3 for its content, and mode.utf8
sets ECI
26. mode.ascii
does not set an explicit ECI mode, as readers are supposed to default to ECI
3 (and even though some default to ECI 26 instead, these share the same codepoints for all ASCII
values).
If you set an ECI which is not compatible with ASCII, do not follow it with a
mode.ascii
section (prefer mode.iso8859_1
or mode.utf8
, as these
will explicitly set the ECI for their content).
auto(text[, options])
mode.auto
will pick the optimal combination of modes for the message. This is used by
default if you provide a plain string to generate
, but you can also use it explicitly (e.g.
if you want to control part of a message but still have automatic compression for other parts):
const code = generate(mode.multi( mode.auto('FOOBAR', { modes: [mode.numeric, mode.iso8859_1], // disallowing alphaNumeric mode }), mode.bytes([0x12, 0x34]), // finish message with non-standard custom data ));
Trailer
ISO 18004 requires 0b11101100_00010001
(0xEC11
) be used as padding bytes at the
end of a message, but you can customise this otherwise dead space with any 16-bit value (0x0000
– 0xFFFF
).
const code = generate('LEAN-QR LIBRARY', { trailer: 0x0000, });
Combining a large minVersion
with a trailer of
0x0000
will reveal the pattern of the chosen mask
, which may be desirable for artistic reasons. In general, this should be left as the default value to
maximise readability.
.with(...modes)
Adds one or more custom modes to the list of default modes.
const myGenerate = generate.with(myCustomMode); const code = myGenerate('text');
This is equivalent to adding the custom mode(s) to the modes
argument in
all calls:
const code = generate('LEAN-QR LIBRARY', { modes: [ mode.numeric, mode.alphaNumeric, mode.ascii, mode.iso8859_1, mode.shift_jis, mode.utf8, myCustomMode, ], });
Note that you only need to register custom modes if you want to use automatic encoding. There is no need to register a custom mode if you call it explicitly:
const code = generate(myCustomMode('FOOBAR'));
Bitmap2D
This class is returned by
generate
and allows you to display the output in several ways:
.toString([options])
Converts the QR Code to a string (e.g. for printing to the terminal)
console.log(code.toString({ on: '##', off: ' ', // note: this is 2 spaces lf: '\n', padX: 4, padY: 4, }));
The options shown are the defaults. Note that for portability, the defaults only use ASCII characters, but
this will not result in a successful read (QR Code scanners require well-defined edges between cells). As
such, if you want to print a scannable QR Code, you will need to replace at least on
with an
alternative value for your environment (see below).
ISO 18004 requires 4-cell padding to guarantee a successful read, but you can change it to any value if you want.
Customising on
and off
If you are printing to a terminal which supports ANSI escape codes, you can use them to define the cell colour:
// Inverted "on" cells: console.log(code.toString({ on: '\u001B[7m \u001B[0m', // ANSI escape: inverted })); // Or explicit black / white cells: console.log(code.toString({ on: '\u001B[40m ', // ANSI escape: black background off: '\u001B[107m ', // ANSI escape: white background lf: '\u001B[0m\n', // ANSI escape: default }));
Ensure the on
and off
strings have the same printed length or the resulting QR
Code will be misaligned.
For displays which do not support ANSI escapes, but do have a line height equal to the character height, you could use Unicode box drawing characters instead:
// Unicode box drawing characters: (not recommended) console.log(code.toString({ on: '\u2588\u2588', }));
.toCanvas(canvas[, options])
Renders the QR Code inside an existing canvas on the page.
const targetCanvas = document.getElementById('my-canvas'); code.toCanvas(targetCanvas, { on: [0x00, 0x00, 0x00, 0xFF], // black off: [0x00, 0x00, 0x00, 0x00], // transparent padX: 4, padY: 4, });
The options shown are the defaults.
This will replace the image in targetCanvas
(which must be a
<canvas>
element) with a copy of the current QR Code. The result is always at a scale
of 1 pixel per module (the canvas
will be resized to the correct size automatically). To
display this image at a reasonable size, it is recommended that you use the following CSS:
#my-canvas { width: 100%; image-rendering: pixelated; }
The values of on
and off
should be arrays in
[red, green, blue, alpha?]
format. If alpha
is omitted, 255 is assumed.
.toImageData(context[, options])
If you do not want to replace the entire content of a canvas, you can can use
toImageData
instead. This returns an ImageData
representation of the QR Code
(created using context.createImageData
).
const myContext = myCanvas.getContext('2d'); const imageData = code.toImageData(myContext, { on: [0x00, 0x00, 0x00, 0xFF], // black off: [0x00, 0x00, 0x00, 0x00], // transparent padX: 4, padY: 4, }); // later myContext.putImageData(imageData, 200, 100);
The options shown are the defaults.
.toDataURL([options])
Returns a string which can be used as a href
, e.g. for downloading;
const url = code.toDataURL({ type: 'image/png', on: [0x00, 0x00, 0x00, 0xFF], // black off: [0x00, 0x00, 0x00, 0x00], // transparent padX: 4, padY: 4, scale: 1, }); const link = document.createElement('a'); link.setAttribute('href', url); link.setAttribute('download', 'my-qr.png'); document.body.append(link);
The options shown are the defaults. You will probably want to set scale
to a larger integer
as a convenience to your users.
This URL can also be used as an img
source, but this is not recommended (for best results use
toCanvas
— this will avoid blurry edges on high
resolution displays and if the user zooms in).
Note that this is only available in-browser; it will fail if called in NodeJS.
.get(x, y)
For custom output formats, you can inspect the data directly:
for (let y = 0; y < code.size; y++) { for (let x = 0; x < code.size; x++) { process.stdout.write(code.get(x, y) ? '##' : ' '); } process.stdout.write('\n'); }
Requests outside the range
0 ≤ x < size, 0 ≤ y <
size
will return false
.
.size
An integer representing the size of the QR Code, excluding any padding. This is equal to
17 + version * 4
.
extras/svg
This is not included in the main library to keep it small, but if you need SVG output, you can access it from a separate import (adds ~2kB).
toSvg(code, target[, options])
Creates or replaces an svg
element with the current QR Code
import { toSvg } from 'lean-qr/extras/svg'; const mySvg = document.getElementById('my-svg'); toSvg(code, mySvg, { on: 'black', off: 'transparent', padX: 4, padY: 4, width: null, height: null, scale: 1, });
The options shown are the defaults.
This will replace the image in mySvg
(which must be an svg
element) with a copy
of the current QR Code. The result is always at a scale of 1 SVG unit per module (the viewBox will be
resized to the correct size automatically). You can define a different size for the SVG element to scale
the image.
If target
is the document
object, this will create and return a new SVG entity
associated with the document (but not attached to it):
const mySvg = toSvg(code, document, {/* options */}); document.body.append(mySvg);
If width
/ height
is given, the root SVG element will have the explicit size
applied. If these are not specified, they will be auto-calculated by multiplying the QR Code size +
padding by scale
. You can override this for display by setting CSS properties (e.g.
mySvg.style.width = '100%'; mySvg.style.height = 'auto';
).
toSvgSource(code[, options])
Like toSvg
but returns the source code for an SVG, rather than
manipulating DOM nodes (can be called inside NodeJS).
import { toSvgSource } from 'lean-qr/extras/svg'; const svgSource = toSvgSource(code, { on: 'black', off: 'transparent', padX: 4, padY: 4, width: null, height: null, xmlDeclaration: false, scale: 1, });
The options shown are the defaults, and match toSvg
.
Returns a complete SVG document which can be written to a standalone file or included inside a HTML document.
If writing to a file, you should set xmlDeclaration
to true
(this prefixes the
source with <?xml version="1.0" encoding="UTF-8" ?>
).
toSvgDataURL(code[, options])
Like toSvg
but returns a data:image/svg+xml
URL containing
the image data, suitable for displaying in an img
tag or downloading from an
a
tag. Can be called inside NodeJS.
import { toSvgDataURL } from 'lean-qr/extras/svg'; const dataURL = toSvgDataURL(code, { on: 'black', off: 'transparent', padX: 4, padY: 4, width: null, height: null, scale: 1, });
The options shown are the defaults, and match toSvg
.
toSvgPath(code)
A raw SVG path definition for the QR Code. Used by toSvg
and
toSvgSource
.
import { toSvgPath } from 'lean-qr/extras/svg'; const svgPath = toSvgPath(code); // e.g. "M1 2L1 1L2 1L2 2ZM3 3L3 2L4 2L4 3Z"
The returned path is always at a scale of 1 SVG unit to 1 module, with no padding. The path will define
the whole QR Code in a single string suitable for use inside <path d="[path here]">
,
and can be used with fill-rule
of either evenodd
or nonzero
. The
path is optimised to ensure only true edges are defined; it will not include overlapping edges (and will
not have "cracks" between pixels). No other guarantees are made about the structure of this string, and
the details could change in later versions.
extras/node_export
This is not included in the main library as it only applies to NodeJS, but if you need PNG output from NodeJS, you can access it from a separate import (adds ~1kB).
toPngBuffer(code[, options])
Returns a Buffer
containing a PNG file for the QR Code.
import { toPngBuffer } from 'lean-qr/extras/node_export'; const pngBuffer = toPngBuffer(code, { on: [0, 0, 0, 255], off: [0, 0, 0, 0], padX: 4, padY: 4, scale: 1, });
The options shown are the defaults.
Returns a complete PNG Buffer which can be written to a standalone file.
toPngDataURL(code[, options])
Like toPngBuffer
but returns a data:image/png
URL
containing the image data, suitable for displaying in an img
tag or downloading from an
a
tag.
import { toPngDataURL } from 'lean-qr/extras/node_export'; const dataURL = toPngDataURL(code, { on: [0, 0, 0, 255], off: [0, 0, 0, 0], padX: 4, padY: 4, scale: 1, });
The options shown are the defaults, and match toPngBuffer
.
extras/react
This import provides convenience wrapper components for React and Preact.
makeAsyncComponent(framework, generate)
Call makeAsyncComponent
from the global scope (not inside a render method) to generate a
component which can be rendered later:
import { generate } from 'lean-qr'; import { makeAsyncComponent } from 'lean-qr/extras/react'; import * as React from 'react'; export const QR = makeAsyncComponent(React, generate);
All the configuration options documented for generate
and
toCanvas
can be passed to the wrapper component, plus a
className
for the rendered canvas
:
<QR content="Hello!" minVersion={1} maxVersion={40} minCorrectionLevel={correction.L} maxCorrectionLevel={correction.H} mask={null} trailer={0xEC11} padX={4} padY={4} on={[0, 0, 0, 255]} off={[0, 0, 0, 0]} className="" />
The options shown are the defaults. All properties are optional except content
.
The component will render to a <canvas>
from a useEffect
hook (this is the
most performant option if the QR Code will change dynamically), but this cannot be server-side rendered
(nothing will appear on the client until hydration completes). If server-side or synchronous rendering is
important to you (at the expense of some performance), use
makeSyncComponent
instead.
You can also change the default values for the component by passing an extra argument to
makeAsyncComponent
. For example, if you want all QR Codes to use at least correction level H:
const QR = makeAsyncComponent(React, generate, { minCorrectionLevel: correction.H, });
makeSyncComponent(framework, generate, toSvgDataURL)
Call makeSyncComponent
from the global scope (not inside a render method) to generate a
component which can be rendered later:
import { generate } from 'lean-qr'; import { makeSyncComponent } from 'lean-qr/extras/react'; import { toSvgDataURL } from 'lean-qr/extras/svg'; import * as React from 'react'; export const QR = makeSyncComponent(React, generate, toSvgDataURL);
All the configuration options documented for generate
and
toSvgDataURL
can be passed to the wrapper component, plus a
className
for the rendered img
:
<QR content="Hello!" minVersion={1} maxVersion={40} minCorrectionLevel={correction.L} maxCorrectionLevel={correction.H} mask={null} trailer={0xEC11} padX={4} padY={4} on="black" off="transparent" className="" />
Note: the API for this has some differences compared to
makeAsyncComponent
: on
and off
take string
values for the colour, rather than
arrays:
<QR content="Hello!" on="black" off="rgba(0,0,0,0)" />
The component will render to an <img>
synchronously, using useMemo
as an
optimisation. This is not the most performant option if the code will change frequently (use
makeAsyncComponent
instead if that is your use-case, to
avoid rendering lag), but supports server-side rendering and avoids the QR Code "flickering" when it first
appears.
extras/errors
All errors reported by the library use codes rather than messages to keep the library size small. You can check the meaning of these codes in the Error Reference. If you want to display the errors to users, this import provides a convenience mapping into English error messages.
readError(error)
Converts errors into human-readable messages. If the error is a lean-qr library error, it will return a
description rather than the code. If the error is from another library, this will extract the
message
property.
import { readError } from 'lean-qr/extras/errors'; try { const code = generate('LEAN-QR LIBRARY'); } catch (e) { const message = readError(e); window.alert(message); }
Error Reference
Errors are reported as numbers to save space. Errors contain a stable code
property which can
be used to look up the type of error:
code | message | meaning |
---|---|---|
1 | lean-qr error 1 | No content provided |
2 | lean-qr error 2 | maxVersion must be ≥ minVersion |
3 | lean-qr error 3 | maxCorrectionLevel must be ≥ minCorrectionLevel |
4 | lean-qr error 4 | content exceeds maximum capacity of maxVersion |
5 | lean-qr error 5 | content cannot be encoded using the chosen modes |
6 | lean-qr error 6 | Invalid framework provided to the React wrapper |
7 | lean-qr error 7 | Invalid generate function provided to the React wrapper |
8 | lean-qr error 8 | Invalid toSvgDataURL function provided to the React wrapper |
If you want to display errors in a moderately human-readable way, you can
use the readError
extra.
Custom Modes
If you want to support a non-standard data format, you can write a custom mode:
const myMode = (value) => (data, version) => { // call data.push zero or more times to encode the value: data.push(0b101010, 6); // value, bits (supports up to 24-bits) }; const code = generate(myMode('foobar'));
If you want your custom mode to be compatible with auto
, you need to provide a pair of properties:
// a function taking a character and returning true if it is suppoerted myMode.test = RegExp.prototype.test.bind(/[0-9a-zA-Z]/); // or myMode.test = (c) => /[0-9a-zA-Z]/.test(c); // a function which estimates the number of bits required for an input // (fractional results will be rounded up) myMode.est = (value, version) => (12 + value.length * 8);
For example, the implementation of ascii
:
const ascii = (value) => (data, version) => { data.push(0b0100, 4); data.push(value.length, version < 10 ? 8 : 16); [...value].forEach((c) => data.push(c.codePointAt(0), 8)); }; ascii.test = RegExp.prototype.test.bind(/[\u0000-\u007F]/); ascii.est = (value, version) => ( 4 + (version < 10 ? 8 : 16) + value.length * 8 );
See modes for details on how to register your custom mode.
Version 2 Migration Guide
Version 2 brings many advantages, including better support for QR features, faster performance, and reduced code size. For basic use, updating from version 1.x to 2.x should have no issues, but if you are using more advanced customisation options or relying on specific behaviours, you may need to update your code:
-
.reg
(regular expression) properties of modes have been replaced with.test
(API compatible withRegExp.test
). Custom modes which integrate withauto
will need updating; -
iso8859_1
mode will now explicitly set the ECI to 3 for better compatibility with some readers. If you only need 7-bit ASCII characters, useascii
mode instead (which does not set the ECI, saving some space).auto
mode will handle this automatically; -
auto
mode is now able to mixutf8
with other modes if it will save space (some QR Codes may change, but the meaning will be the same); -
shift_jis
mode is now available by default (theextras/jis
export has been removed); -
custom modes can now be registered using
updatedGenerator = generator.with(myCustomMode)
, avoiding the need to specify all the default modes; -
toSvgSource
/toSvgDataURL
now acceptrgb()
/rgba()
syntax for colours, matchingtoSvg
; -
toCanvas
/toImageData
/toDataURL
no longer accept 32-bit little-endian integer values for colours. If you were using this legacy behaviour, update your code to pass colours as arrays instead:[red, green, blue, alpha?]
(for example,[255, 0, 255, 255]
= purple); -
errors are now reported as codes to save space. If you want to present errors in a human-readable way, you
can use
extras/errors
to convert; - internal properties and methods are now minified (documented methods are not affected).
Comparison With Other Libraries
Many other QR Code generating libraries exist, with some offering additional out-of-the-box visual features over lean-qr:
library | size / compressed | performance1 (codes per second) |
supported encodings | supported outputs | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
num | alnum | latin-1 | utf-8 | SJIS | automatic | text | canvas |
SVG | PNG | API | other | |||
lean-qr (demo) | 7.2kB / 3.7kB | 2800 / 66 / 35 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅2 | ✅3 | ✅ | - |
qrcode | 23.4kB / 9.0kB | 3200 / 59 / 23 4 | ✅ | ✅ | ❌ | ⚠️5 | ✅6 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - |
qr.js | 25.9kB / 6.5kB | 730 / ❌ / ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | - |
qrcode-generator (demo) | 56.7kB / 11.7kB | 950 / ❌ / 17 | ✅ | ✅ | ✅ | ⚠️5 | ✅7 | ❌ | ✅ | ✅ | ⚠️8 | ❌ | ❌ | img |
qr-creator (demo) | 12.1kB / 5.1kB | 670 / ❌ / ❌ 9 | ❌ | ❌ | ❌ | ⚠️5 | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | Styling: Rounded edges |
awesome-qr | 45.8kB / 16.0kB | untested | ❌ | ❌ | ❌ | ⚠️5 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | Styling: Background image, animated GIF |
QR-Code-Generator (demo) | 45.4kB / 13.3kB | 620 / ❌ / 21 10 | ✅ | ✅ | ❌ | ⚠️5 | ❌11 | ⚠️12 | ❌ | ❌13 | ❌13 | ❌ | ✅ | - |
1 Measured in codes per second (higher is better). See methodology.
2 Requires extra import, adds 1.3kB.
3 Requires extra import, adds 1.1kB.
4 Takes ~13ms to initialise when imported.
5 Non-standard: does not set ECI 26.
6 Requires extra import, adds 21kB.
7 Requires extra import, adds 39kB.
8 In-browser only.
9 Tested in Chrome, as this library does not support NodeJS. Takes ~10ms to initialise when imported.
10 Crudely measured using online demo.
11 Only available in Java version.
12 Automatic mixed modes only available in Java version.
13 Source code contains an example of this output, but it is not part of the library.
Performance Methodology
The performance of each library has been evaluated by running in NodeJS 18.14.0 on a "2.5 GHz Quad-Core Intel Core i7" by repeatedly generating codes (including the output of a string or image, but not including displaying or saving) for each test message with 8 random digits on the end (running "hot" but avoiding caching), and using as much default configuration as possible. Timings have been averaged over a number of samples, with the reported number of codes per second being calculated from the average time. Values are listed to 2 significant figures.
Load / startup time has also been checked, and is noted in a footnote for libraries where it is > 5ms.
The test messages used:
THIS IS MY MESSAGE
(18 characters + 8 random digits)-
The value of π is 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214… (accoring to https://oeis.org/A000796). Having so many digits means it will probably be advantageous to encode it using numeric mode, even though it has Unicode characters around it. WE CAN ALSO USE BLOCK CAPITALS FOR PARTS OF THE MESSAGE, WHICH WILL PROBABLY BE BEST ENCODED AS ALPHANUMERIC. Finally we can use some £ characters which are best encoded in ISO8859-1, though the cost of switching ECI from Unicode means we will need quite a few of them to make it worthwhile ££££££££££££££££££££££££££££££.
repeated 5 times (3155 characters + 8 random digits) A1
repeated 2100 times (4200 characters + 8 random digits)