Interactive.print

Print

Send print jobs to configured printers for labels, documents, and other printable content. Supports ZPL label printing and PDF-to-ZPL conversion for thermal printers.

ctx.ui.interactive.print(config);

Config

jobs*
PrintJob | PrintJob[]

One or more print jobs to execute. Each job specifies the content type, data, and target printer.


title
string

The title displayed on the print card. Defaults to "Print".


description
string

Help text displayed below the title.


autoPrint
boolean

When true, jobs print automatically when the step loads. Default is false.


autoContinue
boolean

When true, the flow continues automatically after printing completes. Default is false.


allowReprint
boolean

When false, hides the print button after the first print. Useful for preventing duplicate labels. Default is true.

Print job properties

Each print job in the jobs array has the following properties:

type*
"zpl" | "rawPdf"

The content type:

  • "zpl" - ZPL commands sent directly to a thermal printer
  • "rawPdf" - PDF document converted to ZPL for thermal printing

name
string

A friendly name for the print job, shown in the print queue.


printer
string

The printer to use, as defined in your keelconfig.yaml. If omitted, uses the user's default printer.


data
string | string[]

The ZPL content to print. Required for zpl type when url is not provided.


url
string

A URL to fetch the print content from. Required for rawPdf type, optional for zpl type.

PDF-specific properties

When using type: "rawPdf", you can configure the PDF-to-ZPL conversion:

dpi
number

The DPI (dots per inch) of the output. Default is 300.


pageWidth
number

The width of the label in dots. For example, a 4" label at 300 DPI is 1200 dots. Default is 1200.


pageHeight
number

The height of the label in dots. For example, a 6" label at 300 DPI is 1800 dots. Default is 1800.

Prerequisites

The print component requires QZ Tray (opens in a new tab) to be installed on the user's workstation. QZ Tray is a desktop application that bridges web applications to local printers.

Setting up QZ Tray

  1. Download and install QZ Tray from qz.io (opens in a new tab)
  2. Launch QZ Tray (it runs in the system tray)
  3. Configure printer mappings in the Keel console under Settings > Devices

Printer mappings

Printers defined in your schema map to physical printers on each workstation. This allows the same flow to work across different warehouse locations where printer names differ.

For example, if your schema defines a printer called "shipping-label", each workstation maps this to their local printer (e.g., "Zebra ZP450" in the shipping office, "Zebra GC420" in the warehouse).

Usage

Printing a ZPL label

Send ZPL commands directly to a thermal printer:

await ctx.ui.page({
  title: "Print shipping label",
  content: [
    ctx.ui.interactive.print({
      title: "Shipping label",
      description: "Print the shipping label for this order",
      jobs: {
        type: "zpl",
        data: `^XA
^FO50,50^ADN,36,20^FDOrder: ${order.reference}^FS
^FO50,100^ADN,36,20^FD${order.customerName}^FS
^FO50,150^ADN,36,20^FD${order.address}^FS
^FO50,250^BY3^BCN,100,Y,N,N^FD${order.trackingNumber}^FS
^XZ`,
        printer: "shipping-label",
      },
    }),
  ],
});

Printing multiple labels

Print multiple labels in a single step:

await ctx.ui.page({
  title: "Print pallet labels",
  content: [
    ctx.ui.interactive.print({
      title: "Pallet labels",
      description: `Printing ${pallets.length} pallet labels`,
      jobs: pallets.map((pallet) => ({
        type: "zpl",
        name: `Pallet ${pallet.reference}`,
        data: generatePalletLabel(pallet),
        printer: "warehouse-label",
      })),
    }),
  ],
});

Printing from a URL

Fetch ZPL content from an external source:

await ctx.ui.page({
  title: "Print label",
  content: [
    ctx.ui.interactive.print({
      jobs: {
        type: "zpl",
        url: `https://labels.example.com/shipping/${order.id}`,
        printer: "shipping-label",
      },
    }),
  ],
});

Printing a PDF as a label

Convert a PDF to ZPL for thermal printing:

await ctx.ui.page({
  title: "Print pick list",
  content: [
    ctx.ui.interactive.print({
      title: "Pick list",
      jobs: {
        type: "rawPdf",
        url: pickList.pdfUrl,
        printer: "warehouse-label",
        dpi: 300,
        pageWidth: 1200,  // 4 inches at 300 DPI
        pageHeight: 1800, // 6 inches at 300 DPI
      },
    }),
  ],
});

Auto-printing

For streamlined workflows, enable auto-print to send jobs immediately:

await ctx.ui.page({
  title: "Shipping",
  content: [
    ctx.ui.display.keyValue({
      data: [
        { key: "Order", value: order.reference },
        { key: "Carrier", value: order.carrier },
      ],
    }),
    ctx.ui.interactive.print({
      title: "Shipping label",
      autoPrint: true,
      allowReprint: false,
      jobs: {
        type: "zpl",
        data: shippingLabel,
        printer: "shipping-label",
      },
    }),
  ],
});

Example: Shipping workflow

A complete example printing shipping labels and packing slips:

export default ShipOrder(async (ctx, inputs) => {
  const order = await ctx.models.order.findOne({
    where: { id: inputs.orderId },
  });
 
  // Generate shipping label
  const shippingLabel = await generateShippingLabel(order);
 
  // Print labels
  await ctx.ui.page({
    stage: "print",
    title: "Print shipping documents",
    content: [
      ctx.ui.display.header({ title: `Order ${order.reference}` }),
      ctx.ui.display.keyValue({
        data: [
          { key: "Customer", value: order.customerName },
          { key: "Items", value: `${order.lineItems.length} items` },
          { key: "Carrier", value: order.carrier },
        ],
      }),
      ctx.ui.interactive.print({
        title: "Shipping documents",
        description: "Print shipping label and packing slip",
        autoPrint: true,
        jobs: [
          {
            type: "zpl",
            name: "Shipping label",
            data: shippingLabel.zpl,
            printer: "shipping-label",
          },
          {
            type: "rawPdf",
            name: "Packing slip",
            url: order.packingSlipUrl,
            printer: "document-printer",
            dpi: 300,
            pageWidth: 2400,  // 8" at 300 DPI
            pageHeight: 3300, // 11" at 300 DPI
          },
        ],
      }),
    ],
  });
 
  // Mark order as shipped
  await ctx.models.order.update({
    where: { id: order.id },
    values: {
      status: "shipped",
      shippedAt: new Date(),
      trackingNumber: shippingLabel.trackingNumber,
    },
  });
 
  return ctx.ui.complete({
    title: "Order shipped",
    content: [
      ctx.ui.display.keyValue({
        data: [
          { key: "Order", value: order.reference },
          { key: "Tracking", value: shippingLabel.trackingNumber },
        ],
      }),
    ],
  });
});

Print queue

The print component displays a print queue showing job status. Users can view the queue by clicking the menu on the print card. The queue shows:

  • Pending - Job waiting to be sent
  • Printing - Job currently printing
  • Complete - Job finished successfully
  • Error - Job failed to print

If a printer mapping is missing, jobs are sent to the user's default printer and a warning is displayed.