I Built a QR Label Designer That Connects Directly to Your Data Tables — Here's How It Works

2 3 13
calendar_today agoschedule4 min read

Ever had to print 200 warehouse bin labels and realized there's no clean way to do it from a web app?

That's what pushed me to build qrlayout-core and qrlayout-ui — two open source npm packages for designing QR label templates and bulk-printing them from React. This post walks through the full workflow with real code.


The Problem

Printing QR labels from a web app usually means one of these:

  • Generating them one at a time manually
  • Hardcoding the label layout in your codebase
  • Exporting a CSV and using a desktop app

None of these scale. And none of them give non-technical users control over the label design.


The Solution: Two Packages, One Pipeline

  • qrlayout-ui — a drag-and-drop label designer your users can embed directly in your app
  • qrlayout-core — the headless rendering engine that turns saved templates + data into PNG, PDF, or ZPL
npm install qrlayout-core qrlayout-ui

The workflow looks like this:

Labels Tab   →  Design a template, save it with a title + entity type
Master Table →  Select that saved layout from a dropdown
Table Rows   →  Check the records you want
Export       →  PNG · PDF · ZPL (thermal printer)

The key idea: template and data stay separate. One layout generates labels for hundreds of records. And because each template is tagged to an entity (employee, machine, storage), the master table only shows layouts relevant to it.


Step 1: Define Entity Schemas

You tell the designer what fields each entity has. This powers the live preview — every {{field}} placeholder dropped on the canvas renders with real sample data instantly.

const SAMPLE_SCHEMAS = {
  storage: {
    label: "Storage Master",
    fields: [
      { name: "binCode",     label: "BIN Code" },
      { name: "storageType", label: "Storage Type" },
      { name: "aisle",       label: "Aisle" },
      { name: "rack",        label: "Rack Number" },
    ],
    sampleData: {
      binCode: "BIN-A1-R4", storageType: "Pallet Rack",
      aisle: "Aisle 01",    rack: "R-44"
    }
  },
  // employee and machine follow the same shape
};

Step 2: Mount the Designer in React

qrlayout-ui is framework-agnostic so you mount it with a ref + useEffect. Two things matter most — passing onSave to capture the layout JSON, and calling destroy() on unmount to prevent memory leaks.

useEffect(() => {
  if (!containerRef.current) return;

  designerRef.current = new QRLayoutDesigner({
    element: containerRef.current,
    entitySchemas: SAMPLE_SCHEMAS,
    initialLayout: editingLayout || { ...DEFAULT_NEW_LAYOUT, id: crypto.randomUUID() },
    onSave: (layout) => {
      storage.addLabel(layout);        // persist the layout JSON
      setLabels(storage.getLabels());
      setSubView('list');
    }
  });

  return () => {
    designerRef.current?.destroy();    // always clean up
    designerRef.current = null;
  };
}, [subView, editingLayout]);

When the user clicks Save inside the designer, onSave fires with the complete StickerLayout JSON — name, target entity, dimensions, and all elements. That single JSON object is what flows through the entire rest of the system.


Step 3: The Master Table — Select Rows, Pick Layout, Export

Each master table (Employees, Machines, Storage BINs) filters layouts by targetEntity so users only see relevant templates in the dropdown.

// Only load layouts relevant to this entity
const binLabels = storage.getLabels().filter(l => l.targetEntity === 'storage');

const getSelectedBins = () => bins.filter(b => selectedBinIds.includes(b.id));
const getActiveLayout = () => labels.find(l => l.id === selectedLayoutId);

const handleExportPDF = async () => {
  const layout = getActiveLayout();
  const selected = getSelectedBins();
  if (!layout) return;
  await exportToBatchPDF({
    layout,
    items: selected,
    printer: printer.current,
    baseFilename: 'batch-bin-labels'
  });
};

When rows are selected, the export toolbar appears with three options. Until then the page shows a step-by-step guide so users know what to do.


Step 4: Three Export Formats

// PNG — one file per item
printer.renderToDataURL(layout, item, { format: 'png' })

// PDF — all items in one document
import { exportToPDF } from 'qrlayout-core/pdf';
const pdf = await exportToPDF(layout, items);
pdf.save('labels.pdf');

// ZPL — for Zebra and other thermal printers
const zplCommands = printer.exportToZPL(layout, items);
// In production: send over TCP port 9100 directly to the printer

exportToPDF is a separate optional import to keep the core bundle light. ZPL output is an array of strings — one per label — which you can download as a .txt file or pipe straight to a Zebra printer over TCP.


Try It — Pre-Loaded Demo Data

The live demo seeds everything on first load:

  • 3 layouts — Professional ID Badge, Equipment Asset Tag, Storage Location Label
  • 3 employees — Arjun Mehta, Priya Sharma, Kiran Patel
  • 3 machines — CNC Router X1, Industrial 3D Printer, Hydraulic Press
  • 3 bins — BIN-A1-R1 (Pallet Rack), BIN-A1-R2 (Shelf), BIN-B2-R1 (Cold Storage)

Go to Storage → check all bins → click PDF. Done in 30 seconds.


This is a Frontend Demo — Here's the Production Path

Everything in the demo runs in the browser using localStorage. In a real project you'd swap each layer with a backend equivalent:

Demo Production
localStorage (layouts) DB table with a layout_json column
localStorage (bins/machines) Your existing ERP or database
Client-side filter by targetEntity WHERE target_entity = 'storage'
Browser PDF download Node.js server-side render → stream to client
.txt ZPL download TCP socket → Zebra printer (port 9100)

qrlayout-core has zero browser dependencies — exportToPDF and printer.exportToZPL work identically in Node.js. The full production flow:

React Frontend
  └── qrlayout-ui  →  user designs label → onSave fires layout JSON
  └── POST /api/layouts  →  save to DB

Node.js Backend
  └── GET /api/layouts?entity=storage  →  filtered layouts to frontend
  └── POST /api/labels/print
        ├── fetch layout from DB
        ├── fetch selected records from ERP / DB
        ├── exportToPDF(layout, items)  →  stream PDF to client
        └── printer.exportToZPL(layout, items)  →  TCP → Zebra printer

Happy to answer questions about the implementation in the comments.

2 Comments

1 vote
0
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

I Built a QR Label Designer That Connects Directly to Your Data Tables — Here's How It Works

Shashidhar Naik - Jul 1

The Sovereign Vault — A Comprehensive Guide to Protocol-Driven AI

Ken W. Algerverified - Jun 4

5 Web Dev Pitfalls That Are Silently Killing Your Projects (With Real Fixes)

Dharanidharan - Mar 3

I Wrote a Script to Fix Audible's Unreadable PDF Filenames

snapsynapseverified - Apr 20
chevron_left
728 Points18 Badges
3Posts
2Comments
15Connections
Software Engineer

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!