qz-tray
License: LGPL-2.1Connects to NanoPrint and sends the jobs.
Version 2.2 or later. Every NanoPrint build is tested with qz-tray.js 2.3.0.
For developers
Your page connects to NanoPrint through qz-tray.js and sends it ESC/POS receipts built with the encoder you prefer. If your app already uses qz-tray.js, it works unchanged.
The ones we recommend, tested with NanoPrint.
Connects to NanoPrint and sends the jobs.
Version 2.2 or later. Every NanoPrint build is tested with qz-tray.js 2.3.0.
Builds ESC/POS bytes: text, code pages, QR codes, images and cut.
Pair it with qz-tray.
Encoder and transports in one package.
Use its QZ Tray transport, installed from npm.
Install both libraries with npm install qz-tray @point-of-sale/receipt-printer-encoder and send a receipt:
import qz from 'qz-tray';
import ReceiptPrinterEncoder from '@point-of-sale/receipt-printer-encoder';
await qz.websocket.connect();
const printer = await qz.printers.getDefault(); // or qz.printers.find('POS-58')
const bytes = new ReceiptPrinterEncoder({ columns: 32 }) // 58 mm; 48 for 80 mm
.initialize()
.line('Corner Shop')
.line('Total: $12.34')
.qrcode('https://example.com/receipt/123')
.cut()
.encode();
const config = qz.configs.create(printer, { jobName: 'Receipt 123' });
await qz.print(config, [
{ type: 'raw', format: 'command', flavor: 'base64', data: toBase64(bytes) },
]);
function toBase64(bytes) {
let binary = '';
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary);
}If the system's default printer is a virtual one (such as "Microsoft Print to PDF"), getDefault() returns null: find the printer by name or let the user pick it.
Optional: NanoPrint shows the name and logo your page sends in its permission prompt, always next to the page's real address. The logo is a PNG, JPEG or WebP data URL of up to 32 KiB.
// Before connecting
qz.security.setCertificatePromise((resolve) => {
resolve(JSON.stringify({
nanoprint: { name: 'Corner Shop', icon: 'data:image/png;base64,…' },
}));
});NanoPrint starts with the computer, but the user may have quit it. Your page can start it again with the nanoprint://start link: the browser asks before opening it, and NanoPrint starts in the tray, with no window.
async function openNanoPrint() {
window.location.href = 'nanoprint://start';
// NanoPrint needs a moment to start
await qz.websocket.connect({ retries: 15, delay: 1 });
}Offer it as an "Open NanoPrint" button after a failed connection: browsers only open apps after a click. The link only starts NanoPrint, carries no data, and the site still needs permission to print.
With NanoPrint installed and running, this page connects to it, lists your printers and prints a sample receipt. It uses only qz-tray.js and ReceiptPrinterEncoder.
NanoPrint is meant to be used by other projects.
nanoprint.nanofactura.com for the download: it detects each user's system and walks them through the installation.wss://localhost:8181 and ws://localhost:8182). If the user picks others, pass them in qz.websocket.connect({ port: … }).