Embed in your page
<zpl-viewer> shows a label as an image in your web page. It renders in the browser with the same renderer as the
server, so the preview is pixel-exact and updates instantly, with no request per preview. It shows only the rendered
label: a PNG image (plus a pager when the ZPL holds more than one label). Users can right-click it to save or copy the
image.
<script type="module" src="https://labelviewer.example.internal/sdk/1.0.1/labelviewer-elements.min.js"
integrity="sha384-<hash from the SDK page>" crossorigin="anonymous"></script>
<zpl-viewer id="preview" zoom="fit"></zpl-viewer>
const viewer = document.getElementById('preview');
viewer.zpl = '^XA^FO50,50^A0N,50,50^FDHello {{name|World}}^FS^XZ';
viewer.data = { name: 'Ada' }; // optional: merged in the browser
Load it in your framework#
Load the SDK once per page: the script tag above (in index.html for Angular, React and Vue apps), or a dynamic
import() of the same URL where the viewer is used. The SDK page has the exact URL and hash for this
install, the versioned and latest URLs, TypeScript types and the CSP your app needs. Then bind the element:
<!-- Angular: add schemas: [CUSTOM_ELEMENTS_SCHEMA] to the component -->
<zpl-viewer [zpl]="zpl()" [data]="record()" zoom="fit"></zpl-viewer>
// React 19 sets the properties directly.
// React 18 only sets string attributes: set zpl and data through a ref instead.
<zpl-viewer zpl={zpl} data={record} zoom="fit" />
<!-- Vue 3: compilerOptions.isCustomElement = (tag) => tag.startsWith('zpl-') -->
<zpl-viewer :zpl="zpl" :data="record" zoom="fit" />
Framework details (zones, React event names, SSR, bundler comments): SDK › Load it per framework.
Attributes and properties#
Attributes are strings; every attribute also has a property (camelCase).
| Attribute | Property | Type | Default | |
|---|---|---|---|---|
zpl | zpl | string | ZPL, or a template with {{placeholders}}. | |
data | data | object | array (attribute: JSON) | none | Merged into zpl in the browser. An array renders one label set per record. Without data, placeholders show their defaults (or their names). |
record | record | integer | null | null | With array data: render only this record (0-based). |
dpmm | dpmm | 6 | 8 | 12 | 24 | 8 | Printhead density (152, 203, 300, 600 dpi). |
width, height | width, height | inches, 0.1-15 | 4, 6 | Label size. |
upright | upright | boolean | true | Ignore ^PO I so the label reads the right way up. upright="false" shows it as it leaves the printer. |
zoom | zoom | fit | actual | number | fit | fit fits the label in the element (the element's width only, when its height is auto). actual = physical size on a 96 ppi screen. A number is CSS pixels per printer dot (1 = 100%; "150%" works too). |
label | label | integer | 0 | The label shown; the pager changes it. |
alt | alt | string | Label preview | Alternative text of the image. |
fonts-base-url | fontsBaseUrl | URL | fonts/ next to the script | Where the font files are. Only needed when the SDK is bundled from npm (SDK › Fonts). |
api-base | apiBase | URL | the install the SDK was loaded from | The labelviewer install whose license applies (SDK). Only needed when the SDK is bundled from npm. |
| — | labelCount (read-only) | number | Labels of the last render. |
When the image is scaled up it keeps hard pixel edges (image-rendering: pixelated). Below one screen pixel per dot it
is scaled smoothly, so thin lines stay visible.
The element is height: 360px by default; set your own height, or height: auto to size it by its width.
Methods#
| Method | |
|---|---|
toPNG(index?) | Promise<Blob>: the label (default: the one shown) as a 1-bit PNG at printer dots, the same bytes the server's PNG route returns (watermarked on a trial license, like the server's). Rejects when the Viewer is not licensed. |
print(index?) | Opens the browser's print dialog with the label at its physical size (through a hidden iframe). |
render() | Render now; returns { labels, warnings }. Changes made in one task are otherwise batched into one render. |
document.getElementById('download').onclick = async () => {
const blob = await viewer.toPNG();
const a = Object.assign(document.createElement('a'), { href: URL.createObjectURL(blob), download: 'label.png' });
a.click();
URL.revokeObjectURL(a.href);
};
Events#
All are CustomEvents that bubble and cross the shadow root.
| Event | detail | When |
|---|---|---|
render | { labels, warnings } | After each render. labels is the number of labels. |
error | { source, message, details? } | source: merge (the data does not fit the template; details lists { record, placeholder, reason }), data (the data attribute is not valid JSON, or record is out of range), render, fonts, license (the install's license does not cover the Viewer: the element shows a notice instead of the label). |
On a merge error the viewer still shows the template with its defaults, plus the message.
Theming#
Custom properties (set them on the element or an ancestor):
| Property | Default | |
|---|---|---|
--lv-viewer-bg | checkerboard | Background around the label |
--lv-label-shadow | soft shadow | Shadow of the label image |
--lv-panel, --lv-border, --lv-radius | #ffffff, #d9dce1, 4px | Pager bar and its buttons |
--lv-text, --lv-font | #1f2933, system UI | Pager text |
--lv-error | #cf222e | Error message text |
Parts for anything else (zpl-viewer::part(image) { ... }):
| Part | |
|---|---|
frame | Everything |
stage | The scroll area around the label |
image | The label image (<img>). canvas still matches it, for stylesheets written for older versions; it will be removed. |
pager | The bar with the previous/next buttons (only shown for more than one label) |
message | The error message |
zpl-viewer { height: 480px; --lv-viewer-bg: #fff; --lv-label-shadow: none; }
zpl-viewer::part(image) { outline: 1px solid #ccc; }
Content Security Policy#
The image is a blob: URL, so your page's CSP needs img-src blob: (besides the script-src and connect-src entries
every page that loads the SDK needs, see SDK › Content Security Policy).