JPEG Buffer renderer
When to use
Section titled “When to use”Use QRCodeJPEGRenderer when Node.js-compatible code must deliver a JPEG file, for example to a
form, print service, or upload field that only accepts JPEG images. JPEG also suits QR codes with a
photographic center image, where lossy compression keeps the photo small.
Prefer the PNG or WebP renderer otherwise:
- Lossy. JPEG blurs sharp edges slightly. QR codes still scan, but pixels near module edges shift a little.
- No transparency. Transparent pixels are blended onto a solid matte color.
- Larger files. For a plain QR code with a few flat colors, PNG and lossless WebP are much
smaller. A default QR code for
https://qrcodesdk.devis about 9.7 KB as JPEG, 1.1 KB as PNG, and 310 bytes as WebP. A block-aligned module size closes most of that gap.
Minimal example
Section titled “Minimal example”import {qrcode} from '@qrcodesdk/core';import {QRCodeJPEGRenderer} from '@qrcodesdk/node';
const jpeg = qrcode('https://qrcodesdk.dev').render(QRCodeJPEGRenderer());Quality and matte example
Section titled “Quality and matte example”import {qrcode} from '@qrcodesdk/core';import {QRCodeJPEGRenderer} from '@qrcodesdk/node';
const jpeg = qrcode('https://qrcodesdk.dev').render( QRCodeJPEGRenderer({ style: {moduleSize: 8, background: '#ffffff00'}, quality: 85, transparency: {matte: '#f8fafc'}, }),);See Customize appearance for every color, shape, and finder option.
Return value
Section titled “Return value”The renderer synchronously returns a Node.js Buffer containing a complete baseline JPEG (JFIF)
file. Its pixel width and height are style.moduleSize × (matrix width + 2 × style.quietZone).
Output above 8192 × 8192 pixels (67,108,864 pixels) throws QRCodeError with code
INVALID_OPTIONS before any pixels are allocated.
Renderer-specific options
Section titled “Renderer-specific options”QRCodeJPEGRendererOptions accepts style, centerImage, quality, and transparency.
TypeScript rejects every other key; the renderer does not repeat that check at runtime.
| Option | Type | Default | Effect |
|---|---|---|---|
style |
QRCodeVisualStyle |
shared defaults | Applies graphical dimensions, colors, and shapes |
quality |
integer 1–100 |
92 |
Trades file size for detail |
transparency.matte |
opaque QRCodeColor |
'#ffffff' |
Color that transparent pixels are blended onto |
centerImage |
QRCodeRasterCenterImage |
undefined |
Draws a center image from rasterCenterImage() |
The default quality of 92 keeps module edges sharp enough for reliable scanning. Lower values make
smaller files with blurrier edges; test the result with the scanners you target before going below
about 75.
Format details
Section titled “Format details”- Baseline JPEG. The file is a single-pass JFIF image. Every JPEG decoder can read it.
- Optimized Huffman tables. The encoder counts the symbols of each image first and fits its Huffman tables to them, which makes files 10–50% smaller than the standard tables at no cost in quality.
- No chroma subsampling. Color is stored at full resolution, so colored module edges stay crisp.
- Grayscale output. When every pixel is gray, such as the default black-on-white style, the file stores one grayscale channel. It is smaller and has no color fringes.
- Transparency. JPEG has no alpha channel. Every pixel is blended onto
transparency.matte, including partially transparent module colors and antialiased edges. Pick the color of the page or print medium behind the QR code. - Identical output everywhere. The encoder uses integer arithmetic only, so the same pixels produce the same bytes in Node.js and in every browser.
Smaller files
Section titled “Smaller files”JPEG compresses the image in blocks of 8 × 8 pixels. When module edges fall inside a block, the
block has to store sharp detail, which costs most of the file size and causes the slight blur around
modules. Choose a style.moduleSize that is a multiple of 8 so every block covers a single module
and is one flat color:
import {qrcode} from '@qrcodesdk/core';import {QRCodeJPEGRenderer} from '@qrcodesdk/node';
const jpeg = qrcode('https://qrcodesdk.dev').render(QRCodeJPEGRenderer({style: {moduleSize: 8}}));style.moduleSize |
Image size | JPEG size |
|---|---|---|
4 |
132 × 132 pixels | 3.0 KB |
5 |
165 × 165 pixels | 9.7 KB |
8 |
264 × 264 pixels | 870 bytes |
Sizes are for https://qrcodesdk.dev at the default quality and quiet zone. A module size of 8,
16, or 24 gives the smallest files and keeps square module edges exact. Sizes that divide 8,
such as 4 or 2, also align modules with the blocks and help less. Rounded or circular module
shapes, center images, and antialiased edges still add detail within blocks.
Center image
Section titled “Center image”Create the center image with rasterCenterImage() from PNG bytes, as for the
PNG renderer. One center image can be reused across the
PNG, WebP, GIF, and JPEG renderers:
import {readFile} from 'node:fs/promises';
import {qrcode} from '@qrcodesdk/core';import {QRCodeJPEGRenderer, rasterCenterImage} from '@qrcodesdk/node';
const centerImage = rasterCenterImage(await readFile('./photo.png'), {size: 0.3});const jpeg = qrcode('https://qrcodesdk.dev') .errorCorrection('H') .render(QRCodeJPEGRenderer({centerImage}));Renderer-specific constraints
Section titled “Renderer-specific constraints”qualitymust be a safe integer from1through100. Other values throwQRCodeErrorwith codeINVALID_OPTIONSwhenQRCodeJPEGRenderer()is called.transparency.mattemust be an opaque#rrggbbcolor, or#rrggbbff. Other values throwQRCodeErrorwith codeINVALID_OPTIONSwhenQRCodeJPEGRenderer()is called.- The center-image source must contain PNG bytes, even for JPEG output. See the PNG renderer constraints for its limits.
- Option values are copied when
QRCodeJPEGRenderer()is called.
Related guides
Section titled “Related guides”- Customize appearance for shared styling and scan reliability.
- Add a center image for reading and sizing a PNG center image.
- Download or save for writing the
Bufferto disk. - Serve a QR code for
image/jpegHTTP responses.
Related pages
Section titled “Related pages”Support QRCodeSDK
If you find QRCodeSDK useful, please consider sponsoring the project!