Download the PHP package bitdreamit/laravel-qz-tray without Composer

On this page you can find all versions of the php package bitdreamit/laravel-qz-tray. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-qz-tray

Laravel QZ Tray

Laravel PHP QZ Tray License

Enterprise silent printing for Laravel. Print PDFs, ZPL labels, ESC/POS receipts, and raw commands directly from your browser — no print dialog, no pop-ups — using QZ Tray's WebSocket bridge.


Quick Start

For the impatient — five commands to a working test print. Every step is explained in full further down; this is just the fast path.

Add the CSRF meta tag and two <script> tags to your layout (see QZ Tray on the machine that will physically print, then visit:

That page shows live connection status, lets you pick a printer, and print a real test job. If it prints, you're done — jump to Frontend Usage for how to wire it into your own pages.


Table of Contents


What is This?

Standard web printing always shows a dialog box. Laravel QZ Tray eliminates that entirely.

How it works:

  1. QZ Tray is a small Java application running on each client machine. It opens a local WebSocket server on port 8181.
  2. Your Laravel app provides a signed certificate (at /qz/certificate) so QZ Tray trusts your website.
  3. SmartPrint.js connects to QZ Tray via WebSocket and sends print jobs silently.
  4. The print job goes directly to the physical printer — no dialog, no confirmation.
Supported print types: Type Description Example Printers
pdf PDF documents Any printer
html HTML content Any printer
zpl Zebra Programming Language Zebra ZD420, ZT230
escpos ESC/POS thermal commands Epson TM, Star Micronics
raw Raw byte commands Any raw-capable printer

Requirements

Requirement Version
PHP 8.1 or higher
Laravel 10, 11, or 12
PHP extension ext-openssl (for certificate generation)
QZ Tray (client) 2.x — installed on each machine that prints

Note: QZ Tray must be installed on every client machine (the computer connected to the printer). It does NOT need to be on your server.


Installation — Step by Step

Step 1 — Install via Composer

Laravel auto-discovers the service provider. Nothing extra needed.


Step 2 — Run the Installer Command

This single command does everything:

Expected output:

Force reinstall: Add --force to overwrite existing files:


Step 3 — Run Migrations

This creates the qz_print_jobs table for optional print job logging.


Step 4 — Add Scripts to Your Layout

Add these two lines to your main Blade layout (e.g. resources/views/layouts/app.blade.php), just before </body>:

Important: The <meta name="csrf-token"> tag is required. SmartPrint uses it to sign print requests.


Step 5 — Install QZ Tray on Client Machines

Each computer that will physically print needs QZ Tray installed.

Download links by OS:

Or link directly in your app:

After installing, QZ Tray starts automatically with Windows/macOS and runs in the system tray. No configuration needed on the client side.


Step 6 — Verify Everything Works

  1. Check status endpoint:

    Should return:

  2. Open the interactive test page:

    This is the official QZ Tray demo — you can test all print types here.

  3. Open the SmartPrint demo page:

    Interactive page showing connection status, printer list, and live print testing.

  4. Test the signing pipeline:

    Should return "message": "Signing works correctly".


Publishing Assets — Manual Control

php artisan qz:install (Step 2 above) runs every publish group at once plus generates a certificate — the right choice for a first install. Reach for the individual vendor:publish commands below when you want more control: republishing only the config after an update, keeping generated views out of a CI pipeline, or a Docker build step that shouldn't touch storage/.

Tag Command Publishes Destination
qz-config php artisan vendor:publish --tag=qz-config config/qz-tray.php config/qz-tray.php
qz-migrations php artisan vendor:publish --tag=qz-migrations qz_print_jobs + qz_printer_preferences migrations database/migrations/
qz-blade php artisan vendor:publish --tag=qz-blade Demo/test Blade views (smart.blade.php, default.blade.php, example.blade.php) resources/views/vendor/qz-tray/
qz-assets php artisan vendor:publish --tag=qz-assets smart-print.js, printer-switcher.js, printer-status.js, adapters, the vendored qz-tray.js, CSS, fonts public/vendor/qz-tray/
qz-installers php artisan vendor:publish --tag=qz-installers QZ Tray desktop installers (Windows/macOS/Linux), if bundled public/vendor/qz-tray/installers/

Publish everything at once without the installer command's certificate-generation step:

Add --force to any of the above to overwrite files you've already published (e.g. after upgrading the package and wanting the latest smart-print.js):

After publishing qz-assets, re-run php artisan migrate if you also re-published qz-migrations — publishing only copies the migration file into your app; it doesn't run it.


Configuration Reference

After publishing, edit config/qz-tray.php:

Protect routes with authentication (recommended for production):


Certificate Management

The certificate allows QZ Tray to trust your website. It is a self-signed SSL certificate generated on your server — only the public certificate is sent to the browser; the private key never leaves your server.

Generate a new certificate

Force regenerate (replace existing)

Show certificate details after generation

Example output:

Customize certificate subject

Edit config/qz-tray.php:

Then regenerate:

Certificate file locations

File Path Purpose
Public certificate storage/qz/digital-certificate.txt Sent to browser at /qz/certificate
Private key storage/qz/private-key.pem Signs requests on server — never exposed

Security: The storage/qz/ directory should not be web-accessible. Laravel's storage/ folder is not served by default — this is correct.


Artisan Commands

Command Description
php artisan qz:install Full install: publish everything + generate cert
php artisan qz:install --force Re-install, overwrite existing files
php artisan qz:install --no-cert Install without generating a certificate
php artisan qz:generate-certificate Generate SSL certificate
php artisan qz:generate-certificate --force Force regenerate certificate
php artisan qz:generate-certificate --show Show certificate details
php artisan qz:clear-cache Clear stored printer preferences (qz_printer_preferences table) for the requesting identity
php artisan qz:clear-cache --session Also clear session-scoped printer data
php artisan qz:clear-cache --all Clear everything, including session data
php artisan qz:prune-preferences Delete qz_printer_preferences rows not updated in the last 90 days (default)
php artisan qz:prune-preferences --older-than=30 Custom age threshold, in days
php artisan qz:prune-preferences --type=session Only prune one identity type (device, user, or session)
php artisan qz:prune-preferences --dry-run Preview what would be deleted, without deleting it

qz:prune-preferences isn't scheduled automatically — the qz_printer_preferences table (unlike the old Cache-backed printer memory) doesn't expire on its own. Wire it into your scheduler if row growth matters for your install:


All Available Routes / API Endpoints

All routes are prefixed with /qz by default (configurable).

Security

Method URL Name Description
GET /qz/certificate qz.certificate Returns the public certificate (plain text)
POST /qz/sign qz.sign Signs data with SHA512 for QZ Tray verification

Status & Health

Method URL Name Description
GET /qz/status qz.status Full status: cert, key, endpoints
GET /qz/health qz.health Simple health check
GET /qz/test/connection qz.test.connection Test API connectivity
POST /qz/test-sign qz.test-sign Test signing pipeline end-to-end

Printer Management

Method URL Name Description
GET /qz/printers qz.printers Info endpoint (actual list comes via WebSocket)
POST /qz/printer qz.printer.set Remember a printer for a URL path
GET /qz/printer/{path} qz.printer.get Get remembered printer for a URL path

Device identity (v1.1.0+): every request to /qz/printer, /qz/print, /qz/jobs, and /qz/clear-cache is scoped by whichever of these identities is present, in the order set by qz-tray.identity_priority (default device → user → session):

A request can match more than one identity at once (e.g. a logged-in user on a device-identified kiosk); POST /qz/printer writes a preference row for every identity present, so switching identity_priority later doesn't lose data. There is no unscoped, identity-less fallback — two different users/workstations can never read each other's stored printer.

Print Jobs

Method URL Name Description
POST /qz/print qz.print Accept and log a print job
GET /qz/jobs qz.jobs List this workstation/user's active print jobs (scoped, see above)
DELETE /qz/jobs/{id} qz.jobs.cancel Cancel a print job by its UUID

Cache & Setup

Method URL Name Description
POST /qz/clear-cache qz.clear-cache Clear printer cache
POST /qz/setup qz.setup Setup info (cert/key status + endpoint URLs)
POST /qz/generate qz.generate Generate cert via HTTP (disabled by default)

Demo & Test Pages

Method URL Name Description
GET /qz/test qz.test Full QZ Tray demo page (all print types)
GET /qz/smart qz.smart SmartPrint interactive demo page
GET /qz/test/pdf qz.test.pdf Generate and stream a test PDF

Installer Downloads

Method URL Name Description
GET /qz/installer/windows qz.installer Windows installer info
GET /qz/installer/linux qz.installer Linux installer info
GET /qz/installer/macos qz.installer macOS installer info

Frontend Usage — SmartPrint JS

SmartPrint is the JavaScript library that lives in public/vendor/qz-tray/js/smart-print.js. It handles the WebSocket connection, signing, queue, retry, and printer memory automatically.


Method 1 — HTML Data Attributes (Zero JS)

The simplest approach. Add data-qz-print to any button or element. No JavaScript needed.


Method 2 — JavaScript API

Full control via the SmartPrint global object.


Method 3 — Global Shorthand Functions

These global helpers are available anywhere on the page:


Print Types Reference

Type data-qz-type value When to use
PDF pdf Standard documents, invoices, reports
HTML html Dynamic web content
ZPL zpl Zebra label printers
ESC/POS escpos Thermal receipt printers (Epson, Star)
Raw raw Any raw byte command

All Data Attributes Reference

Attribute Type Default Description
data-qz-print string URL of the file to print. Required for click-to-print buttons.
data-qz-auto-print string URL to print automatically when element loads.
data-qz-type string pdf Print type: pdf, html, zpl, escpos, raw
data-qz-printer string saved/default Printer name. If omitted, uses saved/default printer.
data-qz-copies number 1 Number of copies to print.
data-qz-data string Raw data for ZPL/ESC/POS/raw types (instead of URL).
data-qz-profile string default Paper profile: default, small (80mm), label (100×150mm)
data-qz-delay number 0 Milliseconds to wait before printing.

Full SmartPrint API Reference

Printing

Printer Management

Print Job Promises (v1.1.0+)

print(), printRaw(), printZPL(), and printESC() all return a Promise that resolves once the job actually reaches the printer (or the browser fallback dialog), and rejects on failure — including if the user cancels the printer-selection modal. Prior releases documented this but it silently resolved to undefined; it now behaves as documented.

Connection

Queue Management

Cache

Settings


Events Reference

Listen to events using SmartPrint.on(event, callback):

Event Payload When it fires
connected { printers: string[] } WebSocket connection successful
connection-failed { error } Could not connect to QZ Tray
disconnected Connection closed
printers-loaded { printers: string[] } Printer list loaded from QZ Tray
printer-saved { printer, scope } User selected/saved a printer
job-queued { job } Print job added to queue
job-processing { job } Job is being sent to QZ Tray
job-completed { job } Job sent successfully
job-failed { job, error } Job failed (moved to offline buffer)
fallback-print { job } Browser print dialog opened as fallback
queue-cleared Queue was cleared
cache-cleared Printer cache was cleared
settings-updated { settings } Settings changed
ready { printers } SmartPrint fully initialized
init-failed { reason } qz-tray.min.js not loaded on page

Remove an event listener:


Printing Use Cases

Print a PDF Invoice

Your Laravel route just returns a PDF response as normal:


Print a ZPL Label (Zebra)

Laravel route returning ZPL:


Print an ESC/POS Receipt (Thermal)

Or generate the receipt server-side and fetch it:


Auto-Print on Page Load

Print automatically when the page loads, with no button click needed:

With a delay (useful for "print after save" flows):

Using JavaScript:


Print with Copies and Delay


Printer Switcher Modal

Show a modal for the user to pick a printer:

Or use the keyboard shortcut: Press Ctrl + Shift + P anywhere on the page.

The modal shows all available printers. The user clicks one and it is saved (per-page by default) in localStorage.

To set a printer programmatically:


Print Queue with Retry

SmartPrint automatically queues jobs and processes them one by one. Jobs that fail are saved to a retry buffer.

Add the built-in queue UI to your page:


Offline Queue (Print When Reconnected)

If QZ Tray is offline when a job is submitted, SmartPrint saves it to localStorage. When QZ Tray reconnects, the jobs print automatically.


Listen to Print Events

Use events to update your UI, track completions, or log to your backend:


Using the ZPL Adapter

The package includes a ZPL helper (public/vendor/qz-tray/js/adapters/zpl.js). Include it alongside smart-print.js:


Using the ESC/POS Adapter

Include adapters/escpos.js:


Laravel Backend Printing (Server-Side)

You can trigger a print from a Laravel controller — for example, after saving an order:

Or redirect with a flash variable:


Database — Print Job Logging

Two tables ship with this package. Both are optional — the package works with neither migrated (job logging and server-synced printer memory just silently no-op), but you'll want both for anything beyond a single-page demo.

qz_print_jobs

Every POST /qz/print request writes a row here (that's what smart-print.js calls internally after each successful qz.print() — see Full SmartPrint API Reference). You don't need to write to it manually.

Column Type Description
id uuid or bigint Primary key — see id_type config. uuid (default) means the id is never a guessable sequential integer and is safe to return straight to the client; also the value GET /qz/jobs/DELETE /qz/jobs/{id} operate on.
tenant_id uuid or bigint, nullable Type follows Multi-Tenant Support below.
user_id / user_type uuid/bigint + string, nullable user_id's type also follows id_type (nullableUuidMorphs() or nullableMorphs()) — set id_type to match your User model's actual key type.
device_id uuid, nullable The workstation/browser that submitted the job (X-Device-Id header — see SmartPrint.getDeviceId()).
printer_name string Name of the printer used
document_url string, nullable URL of the printed document
document_type string pdf, zpl, escpos, raw, html
copies int Number of copies
status string pending, processing, completed, failed, cancelled
error_message text, nullable Populated when a job fails
metadata JSON, nullable Any extra data
processed_at timestamp, nullable When the job was completed/cancelled
created_at / updated_at timestamp Standard Eloquent timestamps

Querying your own job history:

Enable request-level logging (separate from the DB rows above — this writes to your Laravel log file, not the database):

qz_printer_preferences

Server-side backup for printer memory (the client-side source of truth is localStorage; this table exists so a cleared browser profile or a fresh browser on the same physical workstation can pick its printer back up — see Printer Switcher Modal). One row per (tenant_id, identity_type, identity_value, path) combination.

Column Type Description
id bigint Auto-increment primary key
tenant_id uuid or bigint, nullable Type follows Multi-Tenant Support about the uniqueness-constraint tradeoff this implies.
identity_type string device, user, or session — see identity_priority
identity_value string The device UUID, user id, or session id
path string The URL path the printer was remembered for
printer_name string The remembered printer
created_at / updated_at timestamp Standard Eloquent timestamps

Rows aren't pruned automatically — see qz:prune-preferences in Artisan Commands.


Multi-Tenant Support

Every table above is tenant-aware. tenant_id (and qz_print_jobs' user_id/user_type) are natively typeduuid or unsignedBigInteger — and follow id_type: same setting, same convention, one config to change.

This assumes your project uses one PK convention throughout — if your tenant/project table and your User model are both bigint-keyed (or both uuid-keyed), set id_type to match and everything lines up with real, indexed, natively-typed columns instead of an untyped string. If a single project genuinely mixes a bigint tenant table with a uuid-keyed User model (or vice versa) — uncommon, but it happens — this config can only match one of them; you'd need to fork the migration for that specific column if you hit this.

Auto-tagging every request without passing it at every call site — set a resolver once in config, used only when the request doesn't supply tenant_id/project_id explicitly:

Filtering print jobs by tenant:

Scoping printer memory to a tenant too (not just job history) — pass the same tenant_id/project_id on POST /qz/printer, or set it once page-wide from your Blade layout so smart-print.js sends it automatically on every printer-memory and job-logging call:

The GET /qz/jobs queue listing also narrows by tenant when one is present, on top of its existing per-device/per-user scoping — see All Available Routes / API Endpoints.

On qz_printer_preferences specifically: its tenant_id is nullable (no default) regardless of id_type, and — unlike the earlier string-based version of this table — a native uuid column can't use an empty-string sentinel for "no tenant" (Postgres rejects '' outright as invalid UUID format). This means the uniqueness constraint is a slightly weaker safety net for single-tenant uuid-mode installs than it was before: setPrinter()/getPrinter() always match on explicit conditions rather than relying on the DB constraint, so this doesn't affect correctness in normal use, only how hard the database itself guards against a genuine race condition.


Security

Route Protection

By default routes have only web middleware. Add auth in production:

Or apply additional middleware:

CSRF Protection

All POST requests from SmartPrint.js automatically include the CSRF token from the <meta name="csrf-token"> tag. No extra setup needed.

Certificate Security

Rate Limiting

The signing endpoint is the most sensitive — consider adding rate limiting:


Environment Variables Reference

None of these are required — every one has a working default and the package runs fully without a .env entry for any of them. Add only the ones you want to override.

Variable Package default What it controls
QZ_DEFAULT_PRINTER (none — user must pick) Pre-select a printer instead of prompting on first print
QZ_WEBSOCKET_HOST localhost Host QZ Tray's WebSocket listens on
QZ_WEBSOCKET_PORT 8181 Port QZ Tray's WebSocket listens on
QZ_AUTO_GENERATE_CERT false Auto-generate a self-signed cert on boot — dev/local only
QZ_ALLOW_PUBLIC_CERT_GENERATE false Allow cert (re)generation via HTTP — leave false in production
QZ_LOGGING_ENABLED false Write every POST /qz/print request to your Laravel log
QZ_LOGGING_CHANNEL stack Which log channel to use, if enabled
QZ_LOGGING_LEVEL info Log level, if enabled
QZ_JOB_ID_TYPE uuid qz_print_jobs primary key type — uuid or bigint. Read at migration time; set before the first php artisan migrate
QZ_UUID_VERSION v7 v7 (time-ordered, better DB index locality) or v4 (fully random), when QZ_JOB_ID_TYPE=uuid
QZ_API_ENABLED false Expose the stateless Sanctum-protected API routes (routes/api.php) alongside the standard session-based ones

Copy-paste block — every variable at its package default (harmless to add as-is; edit only what you're actually changing):

A realistic production override — name your printer, generate the cert once via php artisan qz:generate-certificate instead of auto-generating it, and turn on job logging:


Troubleshooting

"Certificate not found"

Check that storage/qz/ is writable:

"Could not connect — is QZ Tray running?"

  1. Install QZ Tray from qz.io/download
  2. Start QZ Tray — look for the icon in the system tray
  3. Check it's running on port 8181 (default)
  4. Make sure the browser page is served over http:// or https:// (not file://)

Connection works but prints fail

"CSRF token mismatch" on /qz/sign

Ensure the CSRF meta tag is present:

And that /qz/sign is not excluded in VerifyCsrfToken:

Printer list is empty

The printer list comes from QZ Tray via WebSocket — not from the server. If empty:

  1. Confirm QZ Tray is running (system tray icon visible)
  2. Confirm the browser connected (check /qz/smart for status indicator)
  3. Try clicking "Refresh Printers" on /qz/smart

Printing works in development but not production (HTTPS)

QZ Tray 2.x supports HTTPS. Ensure:

"openssl_sign failed"

The private key may be corrupted. Regenerate:

Clear all caches


File Structure

After running php artisan qz:install, the following files are published:

Package source (inside vendor/bitdreamit/laravel-qz-tray/):


Upgrade Guide

From v0.x to v1.0

  1. Update composer:

  2. Re-publish assets (use --force to overwrite):

  3. Regenerate certificate:

  4. Run migrations:

  5. Update HTML — replace old data-smart-print attributes with data-qz-print:

FAQ

Q: Does this package require a license from QZ Tray? A: QZ Tray Community Edition is free for internal/self-hosted use. A commercial license is required for redistribution. See qz.io/pricing.

Q: Does this work on mobile devices? A: No. QZ Tray is a desktop application. Mobile browsers cannot connect to a local WebSocket server. Mobile devices need to use the standard browser print dialog.

Q: Can multiple users print to different printers at the same time? A: Yes. Each browser tab has its own WebSocket connection to QZ Tray on that machine. Users on different machines print to their own locally-connected printers independently.

Q: What happens if QZ Tray is not installed on a client machine? A: SmartPrint falls back to opening the browser's print dialog (if fallback.enabled = true in config). You can also listen to the connection-failed event and show a download link.

Q: Can I use this with React, Vue, or Livewire? A: Yes. SmartPrint is a plain JavaScript object on window. Call it from any framework:

Q: Can I print to a network printer (not USB)? A: Yes — both USB and network printers work the same way from this package's side, because QZ Tray doesn't maintain its own printer list at all. It reads whatever printers the operating system already has registered, and hands you that exact list via WebSocket (SmartPrint.getPrinters()). So "supported" really means "known to Windows/macOS/Linux":

Either way, once it's registered in the OS, use its exact OS-assigned name as the printer value — same as any USB printer.

Q: Is the private key secure? A: Yes — the private key lives in storage/qz/ which is not web-accessible. It is used only by PHP to sign requests, and is never sent to the client.

Q: The certificate expires after 20 years — do I need to renew it? A: Yes, but after 20 years. You can regenerate at any time with php artisan qz:generate-certificate --force — just ensure the new certificate is pushed to production.


License

MIT License — see LICENSE for details.


Support


Made with ❤️ by Bit Dream IT


All versions of laravel-qz-tray with dependencies

PHP Build Version
Package Version
Requires php Version ^8.1
illuminate/support Version ^10.0|^11.0|^12.0|^13.0
illuminate/database Version ^10.0|^11.0|^12.0|^13.0
illuminate/routing Version ^10.0|^11.0|^12.0|^13.0
ext-openssl Version *
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package bitdreamit/laravel-qz-tray contains the following files

Loading the files please wait ...