retail-print-kit

Print receipts from an Electron app to a thermal printer

webContents.print() goes through the operating system's print dialog and driver, which is slow, cannot kick a cash drawer, and cannot do a partial cut. A point of sale app wants raw printer commands sent straight to the device. retail-print-kit does that from Electron's main process.

Install

npm install retail-print-kit
npm install usb          # only for USB printers
npm install serialport   # only for serial printers

The core is pure JavaScript. usb and serialport are native modules that ship prebuilt N-API binaries, so they normally load in Electron without a rebuild. If you package with asar, keep them unpacked (asarUnpack) like any other native module.

Main process

// main.js
const { ipcMain } = require('electron');
const { Printer, profiles } = require('retail-print-kit');
const { NetTransport } = require('retail-print-kit/net');

function transport(settings) {
  if (settings.kind === 'usb') {
    const { UsbTransport } = require('retail-print-kit/usb');
    return new UsbTransport();
  }
  if (settings.kind === 'serial') {
    const { SerialTransport } = require('retail-print-kit/serial');
    return new SerialTransport(settings.port, { baudRate: 9600 });
  }
  return new NetTransport(settings.host);
}

ipcMain.handle('receipt:print', async (event, { settings, template, data }) => {
  const printer = new Printer(transport(settings), profiles[settings.profile]);
  await printer.open();
  printer.render(template, data);
  await printer.close();
  return { ok: true };
});

ipcMain.handle('drawer:open', async (event, settings) => {
  const printer = new Printer(transport(settings), profiles[settings.profile], { initOnOpen: false });
  await printer.open();
  printer.cashdraw();
  await printer.close();
});

Renderer

// preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('pos', {
  printReceipt: (job) => ipcRenderer.invoke('receipt:print', job),
  openDrawer: (settings) => ipcRenderer.invoke('drawer:open', settings),
});
// in the page
await window.pos.printReceipt({
  settings: { kind: 'net', host: '192.168.1.50', profile: 'epson-tm-t88' },
  template, // JSON from the designer
  data: { store: { name: 'My Store' }, items: [], total: 12.5 },
});

On-screen receipt preview

The browser build renders the same template to HTML with no printer and no Node APIs:

import { render, opsToHtml, profiles } from 'retail-print-kit/browser';
const ops = render(template, data, profiles['epson-tm-t88']);
previewFrame.srcdoc = opsToHtml(ops, profiles['epson-tm-t88']);

Tips

Related: receipt templates and the designer, cash drawer.

Install

npm install retail-print-kit

All guides · Designer · npm · GitHub