Installation
> npm install @liquid-js/qr-code-stylingInstall on Node.js
Since Node.js environment doesn't have native image and DOM capabilities, it requires some additional dependencies:
> npm install @liquid-js/qr-code-styling @liquid-js/qrcode-generator @xmldom/xmldom file-type sharpUsage
To get started, simply create a new QRCodeStyling instance with the data and styling options. By default, corner squares and corner dots have the same shape and fill color as regular dots. When corner dots fill is unspecified, it is inherited from corner squares.
import { QRCodeStyling } from '@liquid-js/qr-code-styling'
const qr = new QRCodeStyling({
data: 'http://example.com/', // QR code data
dotsOptions: {
color: '#4267b2', // Primary color
type: 'rounded', // Dot shape (rounded corners)
size: 10 // Dot size (in pixels)
},
backgroundOptions: {
color: '#d0f4ff', // Background color
margin: 3 // Add 3 dots of space around the code
}
})
// Get the generated QR code
const svgCode = await qr.serialize()
Images
There are multiple ways to add an image to your QR code: center (default), background, and overlay. In center mode, the central portion of the QR code is hidden to fit the image, and margin can be added between the image and the code. In overlay mode, the image is placed over the central portion of the QR code, which works well for irregulary-shaped images with transparent backgroud. In background mode, the QR code is rendered over the image using tiny translucent squares, which is especially suited e.g. for square or round logos with bold, solid colors.
import { QRCodeStyling } from '@liquid-js/qr-code-styling'
/**
* Generate a QR code with centered image
*/
const qrCenterImage = new QRCodeStyling({
data: 'http://example.com/', // QR code data
image: 'https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg',
imageOptions: {
mode: 'center', // Center mode
crossOrigin: 'anonymous', // Sets img.crossOrigin property, used when loading third-party images in browser
margin: 1, // Add 1 dot of spacing around the image
imageSize: 0.50 // Image should cover 50% of the available space; codes with higher error correction level can have larger images
}
})
/**
* Generate a QR code with overlayed image
*
* Overlay mode ignores image margin
*/
const qrOverlayImage = new QRCodeStyling({
data: 'http://example.com/', // QR code data
image: 'https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg',
imageOptions: {
mode: 'overlay', // Overlay mode
crossOrigin: 'anonymous', // Sets img.crossOrigin property, used when loading third-party images in browser
imageSize: 0.80 // Image should cover 80% of the available space
}
})
/**
* Generate a QR code with background image
*/
const qrBackgroundImage = new QRCodeStyling({
data: 'http://example.com/', // QR code data
shape: 'circle',
image: 'https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg',
imageOptions: {
mode: 'background', // Overlay mode
crossOrigin: 'anonymous', // Sets img.crossOrigin property, used when loading third-party images in browser
imageSize: 1.00, // Image should cover 100% of the available space
margin: 2, // Inset the QR code by 2 dots
fill: {
color: 'rgba(255,255,255,0.75)' // Color for the light dots, so the reader can distinghush them from the background image
}
}
})
Image size and error correction
When image is inside a QR code (in center or overlay mode), it covers a portion of the code. Depending on qrOptions.errorCorrectionLevel (L, M, Q, H - low, medium, quartile, and high), the image can cover a different portion of the QR code for the code to remain readable; higher error correction means the image can cover a larger portion of the code, at the expense of the code being visually denser and fitting less data per given size.
imageOptions.imageSize (form 0 to 1) specifies the portion of the available space the image should occupy. A good safe value is around 0.8; higher values will generally work fine, but might cause the code to become unreadable depending on the image, error correcton level, and QR code data. When generating highly customised QR codes, always verify a scanner can still recognise the code.
Plugins
Plugins allow to further customise the generated QR code, either by adding your own shapes for dots and corners, or by modifying the generated SVG once the QR code has been rendered. The drawing hooks used by plugins are drawDot, drawCornerSquare, and drawCornerDot. If a plugin doesn't define a drawing hook, or if the drawing hook doesn't return a value, the next plugin in order is tried, or the builtin drawing function is used.
import { QRCodeStyling, Plugin } from '@liquid-js/qr-code-styling'
import { svgPath, rotateFigure } from '@liquid-js/qr-code-styling/plugin-utils'
const customPlugin: Plugin = {
drawDot: args => {
const {
/**
* Dot size (in pixels)
*/
size,
/**
* Top left corner coordinates, the dot should be drawn within [x, x + size], [y, y + size]
*/
x, y,
/**
* Function to check whether neighbor cells contain dots or no
*
* e.g. `if(getNeighbor(0, -1))` to check the cell above the current cell
*/
getNeighbor,
/**
* Custom pseudo-random generator which ensures random elements don't jump around on re-render
*/
getPRandom,
/**
* Rotation of the drawn element (not needed for symetric shapes)
*/
rotation,
/**
* DOM implementation, global document for browser, xmldom for Node.js
*/
document
} = args
/**
* Rotate the element; when connecting shapes to neigbor cells, set rotation based on values from getNeighbor
*/
return rotateFigure({
...args,
draw: () => {
// Create a SVG path (or other shape) element
const shape = document.createElementNS('http://www.w3.org/2000/svg', 'path')
// Set shape attributes
shape.setAttribute(
'd',
// svgPath template literal convert numeric placeholders to fixed-point representation
svgPath`M ${x} ${y + (size - size) / 2}
v ${size}
h ${size / 2}
a ${size / 2} ${size / 2} 0 0 0 0 ${-size}
z`
)
return shape
}
})
},
drawCornerSquare: args => {
// Add your own corner square shape
// This hook should return a tuple [cornerSquare, cornerSquareMask]
// Mask is used with background image mode to render the required translucent space around a corner square
},
drawCornerDot: args => {
// Draw the dot inside the corner square
},
postProcess: (svg, options) => {
// Handle the generated SVG code, e.g. by adding a border
}
}
const qr = new QRCodeStyling({
data: 'http://example.com/',
plugins: [
customPlugin
]
})
Border plugin
Border plugin can be used to add one or more borders to the generated QR code. A border can contain text, and a text-only appearance can be achieved by setting border color to transparent. When adding text, it's recommended to inline fonts using the FontFacesPlugin to ensure correct rendering on all devices.
Alternatively local fonts can be used; this is the recommended way when generating a QR code for use with other design software.
import { QRCodeStyling } from '@liquid-js/qr-code-styling'
import BorderPlugin from '@liquid-js/qr-code-styling/border-plugin'
import FontFacesPlugin from '@liquid-js/qr-code-styling/font-faces-plugin'
/**
* Generate a QR code with a thin inner border and a thicker outer border
*/
const qr = new QRCodeStyling({
data: 'http://example.com/', // QR code data
plugins: [
new BorderPlugin({
/**
* 100% round (i.e. circular) border
*
* Generally roundness value should be the same as for backgroundOptions.round
*/
round: 1,
/**
* Make the border dimensions scale seamlessly with the QR code
*/
proportional: true,
/**
* Border thickness, proportional to the QR code size (0.05 == 5% of the code)
*/
size: 0.05,
/**
* Dashed border, see https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/stroke-dasharray
*/
dasharray: '2% 1%',
/**
* Margin inside the border (10% of the code size)
*/
margin: 0.1,
color: '#20c9dc'
}),
new BorderPlugin({
round: 1,
/**
* When not using `proportional: true`, sizes are in pixels
*/
size: 60,
color: '#4267b2',
text: {
// Default font configuration (font, fontStyle, fontWeight, color, size)
font: 'sans-serif',
color: '#e9ebee',
size: 30,
top: {
// Font configuration can be set per position
font: 'sans-serif',
fontStyle: 'italic',
fontWeight: 'bold',
color: '#20c9dc',
size: 25,
content: 'Scan the'.toUpperCase()
},
bottom: {
content: 'QR code'.toUpperCase()
}
}
}),
/**
* Embed fonts used by the plugins
*/
new FontFacesPlugin([
{
font: 'sans-serif',
src: 'url(data:font/otf;base64,d09GMgABA...)'
},
{
font: 'sans-serif',
fontStyle: 'italic',
fontWeight: 'bold',
src: 'url(data:font/otf;base64,d09GMgABA...)'
}
])
]
})
Backwards compatibility
Before @liquid-js/qr-code-styling version 5.0.0, the generator only supported a single postProcess hook via applyExtension. Existing extensions can still be used, but should be included as plugins: [{ postProcess: extensionFn }] to maintain forwards compatibility once the deprecated extension interface is removed.