QZ Tray alternative: printing without a Java applet on every desk

QZ Tray prints from a browser to a local printer. It needs Java on the machine and a code-signing certificate for silent printing. Here is when to move past it.

QZ Tray solves a specific problem well: a web application needs to send raw ESC/POS or ZPL to a printer physically attached to the machine the browser is running on, without a print dialog. Browsers deliberately cannot do that, so QZ Tray installs a small Java application locally and the page talks to it over a socket.

The short answer: QZ Tray is client-side by design. It requires Java on every workstation, and a code-signing certificate if you want printing to happen without a confirmation prompt. If the printing is triggered by a business event rather than by the user in front of the screen, moving the call to your server removes both requirements.

Two models, two failure surfaces

QZ Tray Server-side print API
Where the call originates The browser Your backend
Local install Java + QZ Tray, per workstation None
Certificate needed for silent print Yes No
Works when the user is not logged in No Yes
Works on a tablet or phone No Yes
Printer must be physically attached Yes, to that workstation No, reachable over the internet
Raw ESC/POS and ZPL Yes Yes

When QZ Tray is the right tool

Do not switch just because a list of requirements looks long. QZ Tray is well suited when:

  • The person triggering the print is physically at the printer: a counter, a lab bench, a dispatch desk.
  • You print to local hardware you cannot make network-addressable, such as a specialised label or card printer with a proprietary driver.
  • The number of workstations is small and someone administers them.

In those cases the local component is not a burden, it is the point.

Where it starts to hurt

The certificate. Without a signed request, QZ Tray shows a confirmation dialog for every print. Getting rid of it means buying a code-signing certificate, keeping the private key on your server, renewing it, and handling the day it expires, which usually surfaces as "everyone suddenly gets a popup".

Java on every desk. A JRE to install, keep updated, and defend in a security review. In organisations that have spent years removing Java from workstations, this is often a hard blocker.

The user has to be there. Nothing prints when the browser is closed, the session has expired, or the order arrives at 3am. Any workflow that should react to an event rather than to a click is a poor fit.

Support cost at a distance. If your users are customers rather than colleagues, you are now supporting installations on machines you cannot see.

Moving the call to the server

When printing is a consequence of something happening (an order paid, a shipment created, a ticket assigned), the natural place for the call is where that event is already handled:

await fetch(`https://www.expedy.fr/api/v2/printers/${printerUid}/print`, {
  method: 'POST',
  headers: {
    Authorization: `${process.env.API_SID}:${process.env.API_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ printer_msg: ticket, origin: `order-${orderId}` }),
});

No install, no certificate, no browser. The printer is reached through its own outbound connection: either a cloud printer, or an existing USB printer behind a Raspberry Pi adapter, which is the closest equivalent to what QZ Tray was doing locally.

A useful middle ground

You do not have to choose globally. Many teams keep QZ Tray for the operator-driven prints at the counter, and move event-driven prints (order confirmations, kitchen tickets, shipping labels generated by a batch job) to the server. The two coexist without conflict, and the server-side path is the one that keeps working at night.

Start by printing a test ticket on the free plan from the backend that already handles the event.

FAQ

Why does QZ Tray need a certificate?

Without a signed request the user gets a confirmation dialog before every print. A code-signing certificate lets your application sign requests so printing is silent, which means buying, storing and renewing that certificate.

Can I print from a browser without any local software?

Not raw ESC/POS to an attached printer: browsers block that deliberately. You can, however, have your server send the job to a network-reachable printer, which removes the browser from the path entirely.

Does a server-side API support raw ESC/POS and ZPL?

Yes. Raw command data can be sent through the print API, so label and receipt formats you already generate keep working.

Can I keep QZ Tray for some prints?

Yes, and it is often the right design. Keep it for operator-driven prints at the counter and move event-driven prints to the server; the two do not conflict.