npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

qrlayout-ui

v1.1.0

Published

Embeddable drag-and-drop QR label designer — works in React, Vue, Svelte, Angular and vanilla JS. Pairs with qrlayout-core.

Readme

qrlayout-ui

An embeddable, framework-agnostic drag-and-drop QR label designer for React, Angular, Vue, Svelte, and plain HTML.

npm version npm downloads License: MIT TypeScript GitHub Stars

Drop a professional label designer into your app in minutes. Supports drag-and-drop element placement, live preview, dark mode, data binding with {{variables}}, and layout export to JSON.

Part of the QR Layout Tool monorepo.


🚀 Live Demos & Examples

Try the designer — no signup needed:

| Framework | Live Demo | Source Code | | :--- | :--- | :--- | | React | ▶ Open Demo | Source | | Angular | ▶ Open Demo | Source | | Svelte 5 | ▶ Open Demo | Source | | Vue 3 | ▶ Open Demo | Source |

QR Layout Designer Screenshot


✨ Features

  • Framework Independent: Built with vanilla TypeScript — mount it inside React, Vue, Angular, Svelte, or a plain HTML page.
  • Drag & Drop Designer: Visual placement and resizing of text and QR code elements on a canvas.
  • Live Preview: Preview your label with real sample data as you design.
  • Data Binding: Bind any field like {{name}}, {{id}}, or {{department}} from your entity schema.
  • Multi-Variable QR: Set a separator (e.g. |) on QR elements to automatically join multiple fields into one scan.
  • Rich Text Styling: Customize font family, size, weight, color, and both horizontal and vertical alignment.
  • Dark Mode: Built-in light and dark themes that follow your app's color scheme.
  • Flexible Units: Design in millimeters, centimeters, inches, or pixels.
  • JSON Output: Save the layout as a compact JSON object to your backend or localStorage.
  • Rendering via qrlayout-core: The same layout JSON drives PNG, PDF, and ZPL export — design once, output anywhere.

📦 Installation

npm install qrlayout-ui qrlayout-core

🚀 Getting Started

Step 1 — Import the CSS

Add this to your project's entry point (e.g., main.ts, index.js, App.tsx):

import "qrlayout-ui/style.css";

Step 2 — Mount the Designer

import { QRLayoutDesigner } from "qrlayout-ui";

const designer = new QRLayoutDesigner({
  element: document.getElementById("designer-container"),

  // Optional: entity schema for {{variable}} data binding
  entitySchemas: {
    employee: {
      label: "Employee",
      fields: [
        { name: "name",       label: "Full Name"    },
        { name: "id",         label: "Employee ID"  },
        { name: "department", label: "Department"   },
      ],
      sampleData: {
        name: "Alice Johnson",
        id: "EMP-001",
        department: "Engineering"
      }
    }
  },

  // Optional: load an existing saved layout
  initialLayout: {
    id: "1",
    name: "My Badge",
    targetEntity: "employee",
    width: 100,
    height: 60,
    unit: "mm",
    backgroundColor: "#ffffff",
    elements: []
  },

  // Fires when the user clicks "Save Layout"
  onSave: (layout) => {
    localStorage.setItem("my-layout", JSON.stringify(layout));
    console.log("Layout saved:", layout);
  }
});

Step 3 — Size the Container

The designer fills 100% of its parent element. Give the container a fixed size:

#designer-container {
  width: 100%;
  height: 100vh;   /* full-screen */
}

Or embed it inside a modal/panel:

#designer-container {
  width: 100%;
  height: 700px;
}

Step 4 — Cleanup on Unmount

designer.destroy();

⚙️ Options Reference

| Option | Type | Required | Description | |---|---|---|---| | element | HTMLElement | ✅ | The DOM element to mount the designer into | | entitySchemas | Record<string, Schema> | ❌ | Entity field definitions for {{field}} binding and live preview | | initialLayout | StickerLayout | ❌ | Layout to pre-load on mount | | onSave | (layout: StickerLayout) => void | ❌ | Callback when "Save Layout" is clicked |


💾 Layout Persistence & Printing Workflow

The onSave callback gives you the complete layout as a plain JSON object. That JSON is your single source of truth — store it anywhere, load it back anytime, and pass it with real data to qrlayout-core for printing.

The Full Cycle

  ┌──────────────────────┐
  │   qrlayout-ui        │  ← User designs the label visually
  │   (Designer)         │
  └──────────┬───────────┘
             │ onSave(layout JSON)
             ▼
  ┌──────────────────────┐
  │   Your Backend /     │  ← Save layout JSON to your DB, file, or localStorage
  │   Database           │
  └──────────┬───────────┘
             │ Load layout JSON + real employee/machine data
             ▼
  ┌──────────────────────┐
  │   qrlayout-core      │  ← Merge data into template, export PNG / PDF / ZPL
  │   (StickerPrinter)   │
  └──────────────────────┘

Step 1 — Save the Layout to Your Database

const designer = new QRLayoutDesigner({
  element: document.getElementById("designer"),
  onSave: async (layout) => {
    // layout is a plain JSON object — save it wherever you like
    await fetch("/api/layouts", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(layout)
    });
  }
});

Step 2 — Load the Layout Back (with initialLayout)

// Fetch a saved layout from your backend
const saved = await fetch("/api/layouts/employee-badge").then(r => r.json());

const designer = new QRLayoutDesigner({
  element: document.getElementById("designer"),
  initialLayout: saved,   // ← the JSON you saved earlier
  onSave: (layout) => { /* update in DB */ }
});

Step 3 — Print with Real Data (via qrlayout-core)

import { StickerPrinter } from "qrlayout-core";

const printer = new StickerPrinter();

// Fetch layout + real records from your backend
const layout  = await fetch("/api/layouts/employee-badge").then(r => r.json());
const records = await fetch("/api/employees").then(r => r.json());

// Batch export — one PNG/PDF/ZPL per record
const zplPages = printer.exportToZPL(layout, records);

// or export as PDF
const pdf = await printer.exportToPDF(layout, records);
pdf.save("batch-badges.pdf");

[!NOTE] About the live demo applications — The demo apps (React, Angular, Svelte, Vue) ship with a small set of pre-built sample layouts and employee/machine records so you can explore all features immediately without any setup.

All demo data is stored exclusively in your browser's localStorage. Nothing is sent to or stored on any server. Clearing your browser storage resets the demo to its defaults. Your designs and data never leave your browser.


🔌 Integration Examples

React (TypeScript)

import { useEffect, useRef } from 'react';
import { QRLayoutDesigner } from 'qrlayout-ui';
import 'qrlayout-ui/style.css';

const LabelDesigner = () => {
  const containerRef = useRef<HTMLDivElement>(null);

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

    const designer = new QRLayoutDesigner({
      element: containerRef.current,
      onSave: (layout) => console.log('Saved:', layout)
    });

    return () => designer.destroy();
  }, []);

  return <div ref={containerRef} style={{ width: '100%', height: '100vh' }} />;
};

export default LabelDesigner;

Vue 3 (Composition API)

<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import { QRLayoutDesigner } from 'qrlayout-ui';
import 'qrlayout-ui/style.css';

const container = ref(null);
let designer = null;

onMounted(() => {
  designer = new QRLayoutDesigner({
    element: container.value,
    onSave: (layout) => console.log('Saved:', layout)
  });
});

onUnmounted(() => {
  if (designer) designer.destroy();
});
</script>

<template>
  <div ref="container" style="width: 100%; height: 100vh;" />
</template>

Angular (v17+ Standalone Component)

import { Component, ElementRef, ViewChild, OnInit, OnDestroy } from '@angular/core';
import { QRLayoutDesigner } from 'qrlayout-ui';
import 'qrlayout-ui/style.css';

@Component({
  standalone: true,
  selector: 'app-label-designer',
  template: '<div #container style="width: 100%; height: 100vh;"></div>'
})
export class LabelDesignerComponent implements OnInit, OnDestroy {
  @ViewChild('container', { static: true }) container!: ElementRef;
  private designer?: QRLayoutDesigner;

  ngOnInit() {
    this.designer = new QRLayoutDesigner({
      element: this.container.nativeElement,
      onSave: (layout) => console.log('Saved:', layout)
    });
  }

  ngOnDestroy() {
    this.designer?.destroy();
  }
}

Svelte 5 (Runes)

<script lang="ts">
  import { onMount } from 'svelte';
  import { QRLayoutDesigner } from 'qrlayout-ui';
  import 'qrlayout-ui/style.css';

  let container = $state<HTMLDivElement | null>(null);
  let designer: QRLayoutDesigner | null = null;

  onMount(() => {
    if (!container) return;

    designer = new QRLayoutDesigner({
      element: container,
      onSave: (layout) => console.log('Saved:', layout)
    });

    return () => designer?.destroy();
  });
</script>

<div bind:this={container} style="width: 100%; height: 100vh;" />

Vanilla JavaScript / HTML

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="node_modules/qrlayout-ui/dist/style.css">
  <style>
    #designer { width: 100%; height: 100vh; }
  </style>
</head>
<body>
  <div id="designer"></div>

  <script type="module">
    import { QRLayoutDesigner } from 'qrlayout-ui';

    const designer = new QRLayoutDesigner({
      element: document.getElementById('designer'),
      onSave: (layout) => {
        localStorage.setItem('layout', JSON.stringify(layout));
      }
    });
  </script>
</body>
</html>

🔗 Related

  • qrlayout-core — Use the same layout JSON to render PNG, PDF, or ZPL without the UI
  • GitHub Repository — Full monorepo, issue tracker, and discussions

📄 License

MIT © Shashidhar Naik