Skip to content

For developers

Print ESC/POS receipts from JavaScript

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.

Integration guide Protocol

Compatible libraries

The ones we recommend, tested with NanoPrint.

qz-tray

License: LGPL-2.1

Connects to NanoPrint and sends the jobs.

Version 2.2 or later. Every NanoPrint build is tested with qz-tray.js 2.3.0.

web-escpos-printer

License: MIT

Encoder and transports in one package.

Use its QZ Tray transport, installed from npm.

Minimal example

Install both libraries with npm install qz-tray @point-of-sale/receipt-printer-encoder and send a receipt:

JavaScript
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.

Your site's name and logo

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.

JavaScript
// Before connecting
qz.security.setCertificatePromise((resolve) => {
  resolve(JSON.stringify({
    nanoprint: { name: 'Corner Shop', icon: 'data:image/png;base64,…' },
  }));
});

When NanoPrint is closed

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.

JavaScript
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.

Try it now

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 will ask whether this site may print.
  • Chrome may ask to allow access to devices on your local network: allow it so the page can reach NanoPrint.

Offer it to your users

NanoPrint is meant to be used by other projects.

  • Link to nanoprint.nanofactura.com for the download: it detects each user's system and walks them through the installation.
  • It is free for you and your users: no per-seat licenses, subscriptions or usage limits.
  • Your app only talks to NanoPrint, a separate program, so it can have any license, including a proprietary one.
  • If something is missing or broken, tell us on GitHub: real integrations help us decide what comes next.
Open an issue on GitHub

Before you integrate

  • https pages: the user enables "secure sites" in NanoPrint once; NanoPrint offers it right after installing.
  • Chrome may ask for local network access before the page can connect to NanoPrint.
  • Ports: the same as qz-tray.js (wss://localhost:8181 and ws://localhost:8182). If the user picks others, pass them in qz.websocket.connect({ port: … }).
  • Limits: 30 jobs a minute per site, up to 10 copies per job and messages of up to 2 MiB.
  • Raw printing only: files, serial ports, USB, HID and pixel, HTML or PDF printing are not available.
Full documentation on GitHub