Zebra labels, written in TypeScript

node-zpl builds ZPL from typed code or JSX, checks it for mistakes the printer won't report, and shows you the label before anything is printed.

npm install node-zpl
const label = build(
  <Label width={mm(101.6)} height={mm(50.8)} dpmm={8}>
    <Text x={mm(5)} y={mm(5)} height={mm(7)}>
      Harbor Parts Co.
    </Text>
    <Text x={mm(5)} y={mm(14)} height={mm(4)}>
      Order #{order.id} · Shelf {order.shelf}
    </Text>
    <Box x={mm(5)} y={mm(21)} width={mm(91)}
         height={dots(3)} thickness={dots(3)} />
    <Code128 x={mm(5)} y={mm(25)} height={mm(13)}>
      {`${order.id}-${order.shelf}`}
    </Code128>
    <DataMatrix x={mm(76)} y={mm(25)} moduleSize={dots(5)}>
      https://zpl.fingal.dev
    </DataMatrix>
    {order.express && (
      <Text x={mm(66)} y={mm(5)} height={mm(7)}>EXPRESS</Text>
    )}
  </Label>
);

label.toZPL();
A 4 by 2 inch shipping label: Harbor Parts Co., order 10442 on shelf B_3, an EXPRESS marker, a Code 128 barcode and a Data Matrix code

101.6 × 50.8 mm

Build labels with types

Write labels as JSX or method calls. Every length takes a unit, mm() or dots(), so a bare number is a type error, and text is escaped for you.

<Text x={mm(5)} y={mm(5)}>
  Hello
</Text>

Catch what the printer won't tell you

Printers quietly clamp values, ignore commands and skip fields. The linter finds those problems in labels you build and in ZPL you already have.

lint(parse(zpl).commands);
// The box is 1 dot wide but its
// line is 2 dots thick, so the
// printer widens it to 2 dots.

Preview anywhere

A web component renders labels in the browser for React, Vue, Svelte, Angular or plain HTML. Label data never leaves the page.

<zpl-preview
  zpl={label}
  dpmm={8}
/>