Link2Label

Make labels, fill them with your item data, and print.

Table of contents

Getting started

Link2Label lets you design labels, drop in text and codes from your inventory app, and print. You can start from scratch, from a ready-made template, or from data someone sends you.

Home screen tabs

  • New — pick a size and draw a blank label.
  • Saved — labels you already made. Open, duplicate, export, or delete.
  • Templates — reusable layouts. Fill them with data or copy and customize them.

Quick paths

  • Blank label: New tab → pick a size → design → print or export.
  • From a link or file: tap a link, paste JSON, pick a file, or import a photo with embedded data.
  • From a template: see the two template workflows below.

Working with templates

Copy an included template and edit it

Included templates ship with the app. They are a good starting point if you want your own layout without building from zero.

  1. Open the Templates tab.
  2. Scroll to Included templates at the bottom.
  3. Tap the copy icon on the template you want.
  4. A copy appears under Your templates (named like "Strip QR Title copy").
  5. Tap the copy to open it in the template editor.
  6. Change text placeholders, QR layout, fonts, or add images and shapes as you like.
  7. Your copy is yours to keep. The original included template stays unchanged.

Fill a template, save as a label, then edit freely

Use this when you want one finished label, not a reusable template. After you save, the label lives under Saved and you can change anything without touching the template.

  1. Open the Templates tab.
  2. Tap the tags (fill) icon on a template, or use Import item data to load JSON first.
  3. Fill in the fields in the form, or let imported data fill them for you.
  4. Review the preview (fill-review screen).
  5. Tap Save (disk icon in the header). The label is stored under Saved.
  6. Open it from the Saved tab. It is a normal label now: edit text, move objects, add stamps, then print.

You can also fill from item data (paste, JSON file, link, or photo) and save the result the same way from fill-review.

Header buttons

  • Menu (top left) — settings, printer setup, help, ingress link tool.
  • Import item data — paste JSON or pick a .json file.
  • Import image — pick a photo. If it has embedded item data, labels are filled automatically. Otherwise you get a simple photo label.
  • Plus — new label or new template, depending on the tab.
Pre-alpha: Link2Label has not shipped yet. Saved files from older versions may not open after an update.

Label editor

The canvas shows your label at real print size. Objects snap to a grid. Undo and redo are in the header.

Tools (bottom strip)

  • Select — move, resize, rotate.
  • Draw — freehand lines.
  • Insert — text, text box, lists, images, QR, barcode, shapes, frames, SVG, dates, stamps, map markers.
  • Shapes — rectangles, circles, lines, arrows.
  • Stamps — decorative icons.

When something is selected

A toolbar appears for alignment, delete, and type-specific options (font, QR content, list layout, map marker link, and so on).

Header

  • Undo / redo.
  • Print.
  • Overflow (⋯) — settings, export PNG/PDF, save, duplicate, delete.

Two modes

  • Label — a one-off design under Saved.
  • Template — a reusable layout with placeholders and data mappings under Templates.

Grid spacing and units (metric/imperial) are in Menu → Settings.

Printing

On iPhone and Android you can print to Bluetooth label printers. If that fails or you are on web, the normal system print dialog is used.

Supported printers

FamilyExamplesPaper
Marklife P12 and similar (name starts with P12_) Continuous roll
Cat-Printer GB01, GB02, GB03, GT01, MX05–MX10, YT01 Portrait sticker

Unknown printer? Add it as generic Marklife or Cat-Printer in Printer settings and tune options.

Setup

  1. Menu → Settings → Printer settings.
  2. Scan and connect your printer.
  3. Set it as default if you like (also from the print sheet).

What happens when you print

  • Bluetooth printer ready → prints directly.
  • Bluetooth fails → falls back to system print dialog.
  • No Bluetooth printer → system print dialog.
  • Web app → system print dialog only (no Bluetooth in browser).

Auto-print from item data

If JSON includes "action": "fill_and_print", the app fills and prints without an extra tap. Large batches ask for confirmation first (threshold in Settings → Template ingress).

Platforms

Platform Editor BLE print Share extension Deep links EXIF import
iOS Yes Yes Yes link2label:// Yes
Android Yes Yes Yes (intent) link2label:// Yes
Web Yes No No HTTPS /ingress/… Limited

Native share-to-Link2Label (text, URLs, JSON) requires a store build with the share extension. It is disabled in Expo Go.

Templates

Templates are layouts you use again and again. Placeholders (like {title} or a QR slot) get filled from item data or from a manual form.

Included vs yours

  • Included templates — built into the app, listed at the bottom of the Templates tab. Good defaults; also used by Containd.
  • Your templates — ones you created or copied. You can rename, duplicate, export, and delete them.

Tip: copy an included template first if you want your own version without changing the original. See Getting started for step-by-step.

Editing an included template directly changes that included copy (Containd still finds it by catalog ID). Use Reset to default (refresh icon) to undo your edits to an included template.

Included catalog IDs

Catalog IDUse case
containd-box-labelBox/container labels with content tree (Containd preset: box-label)
containd-strip-labelCompact strip labels (Containd preset: strip-label)
strip-qr-title-01-templateStrip with title and QR
strip-qr-only-01-templateStrip with QR only
strip-barcode-01-templateStrip with barcode
sticker-qr-title-01-templateSticker with title and QR
sticker-qr-only-01-templateSticker with QR only
full-box-sticker-01-templateFull box sticker layout
image-full-box-01-templateFull-page image label
image-qr-sticker-01-templateImage sticker with QR
map-full-page-01-templateFull-page map label
map-full-page-square-01-templateSquare full-page map
map-sticker-01-templateMap sticker

Manual fill

Tap the tags icon on a template row to open a form. Type values by hand without any JSON file or link. Good for one-off labels or trying a layout.

Data mappings

Mappings tell the app which item field goes to which placeholder (for example title → title text box). They live inside each template. Open the mapping editor from a template to set them up. See Mapping reference.

After filling (fill-review)

You see a preview before printing. From here you can:

  • Save to Saved labels (then edit freely under Saved).
  • Switch to a different template (same data).
  • View the contents tree on box labels.
  • Shrink the page to fit content.
  • Go back to Containd when a return link was sent.

Export / import

Export a template as a .labeltemplate.json file from the share icon on its row. Import from the Templates tab menu.

Map labels

Map labels show a floor plan or photo with numbered markers and a legend list. You can build them by hand or send coordinates in JSON.

From item data (ingress)

Use format label-containd-map-ingress. Send a list of items:

  • First item (index 0) = map title and overall frame.
  • Other items = regions with x, y, width, height on the shared image.
  • Optional colors: backgroundColor, textColor, borderColor.
  • Optional canvasW / canvasH for the coordinate system.

After import, pick the map photo when asked, then choose a map template (for example map-full-page-01-template).

Map ingress JSON example

Index 0 is the map itself. Other entries are regions with x/y/width/height on the shared image.

{
  "format": "label-containd-map-ingress",
  "version": 1,
  "senderId": "containd",
  "returnDeepLink": "containd://location/warehouse-a",
  "data": [
    {
      "title": "Warehouse A — Floor plan",
      "fullDeepLink": "containd://location/warehouse-a",
      "canvasW": 1200,
      "canvasH": 800,
      "x": 0, "y": 0, "width": 1200, "height": 800
    },
    {
      "title": "Shelf A1",
      "description": "Electronics",
      "x": 120, "y": 80, "width": 140, "height": 90,
      "backgroundColor": "#FFFFFF",
      "textColor": "#000000",
      "borderColor": "#333333"
    },
    {
      "title": "Shelf B2",
      "x": 420, "y": 220, "width": 120, "height": 80
    }
  ]
}

Build a map by hand

On any label that already has an image and a list:

  1. Insert → Map marker to place numbers on the image.
  2. Select a marker → Link to legend row.
  3. Select the list → List style → Legend markers.

Sending item data

Other apps (like Containd) can send item details into Link2Label as JSON. The app reads that JSON and fills your template placeholders.

Basic payload shape

Every payload needs format, version, and data. Optional extras control which template to use and what happens next.

Minimal payload with command
{
  "format": "label-containd-ingress",
  "version": 1,
  "senderId": "containd",
  "returnDeepLink": "containd://item/abc123",
  "data": {
    "title": "Storage Box A-12",
    "productCode": "4007817326004"
  },
  "command": {
    "action": "fill_and_show",
    "templateId": "containd-strip-label",
    "mappingId": "default"
  }
}
  • data — one item object, or an array for a batch of labels.
  • command — optional. Says which template to use and whether to show preview, print, etc. Defaults to preview with the default mapping.
  • senderId + returnDeepLink — optional. Lets the user jump back to the sending app after printing.

Two different "return" links

FieldWhereWhat it does
returnDeepLink Top level of payload Opens the sender app again (Back to Containd button)
fullDeepLink Inside data URL printed in the QR code on the label

How data can arrive

  • Tap a link2label:// link (native app).
  • Open an https://…/ingress/… link (web app).
  • Paste JSON or pick a .json file.
  • Share text or a URL to Link2Label (native).
  • Import a JPEG with JSON embedded in the file (see Image, EXIF, and file import).

JSON structure

One item or many

  • One item: "data": { "title": "Widget" }
  • Many items: "data": [ { … }, { … } ] — one label per item in the batch. Use a JSON file, paste, or base64 link (not the short flat URL format).

Formats

  • label-containd-ingress — normal labels (strips, stickers, box labels).
  • label-containd-map-ingress — map labels with regions on an image.

Item fields (all optional)

Only include fields your template actually uses.

FieldTypeTypical use
title, descriptionstringMain text on the label
productCode, internalProductCodestringBarcodes, small print
fullDeepLinkstringURL for the QR code
location, owner, conditionstringExtra lines of text
amount, amountUnit, contentNumbernumber / stringCounts and quantities
createdAt, etc.numberDates as Unix milliseconds
valueNew, valueNow, currency, weightnumber / stringValue lines
tagList, categoriesstring[]Tag lists
arbitraryFieldsListobject[]Custom fields: { categoryId, propertyId, value }
contenttree[]What's inside a box (see tree example below)
x, y, width, heightnumberMap regions only
Content tree (content field)

Each node has t (title). Child nodes go in c.

"content": [
  {
    "t": "Electronics",
    "c": [
      { "t": "HDMI cables" },
      { "t": "USB adapters", "c": [{ "t": "USB-C hub" }] }
    ]
  },
  { "t": "Tools", "c": [{ "t": "Screwdriver set" }] },
  { "t": "Labels & tags" }
]

Command actions

actionWhat happens
fill_and_showFill and show preview
open_mappingOpen mapping editor
fill_and_export_imageFill and export PNG
fill_and_printFill and print (if a printer is set up)

Print options (command.options)

FieldEffect
printerIdUse a specific saved Bluetooth printer
autoPrint: falseShow print sheet instead of printing immediately
fitToContent: trueShrink label to fit content
returnToSender: falseDo not open Containd after print
Single item with content tree (box label)
{
  "format": "label-containd-ingress",
  "version": 1,
  "data": {
    "title": "Storage Box A-12",
    "location": "Garage · Shelf 3",
    "description": "Winter equipment and cables",
    "productCode": "BOX-A12",
    "fullDeepLink": "https://example.com/item/box-a12",
    "contentNumber": 9,
    "content": [
      {
        "t": "Electronics",
        "c": [{ "t": "HDMI cables" }, { "t": "USB adapters" }]
      },
      { "t": "Tools", "c": [{ "t": "Screwdriver set" }] }
    ]
  },
  "command": {
    "action": "fill_and_show",
    "templateId": "containd-box-label",
    "mappingId": "default"
  }
}
Multi-item batch (two cameras)

Each object in data becomes one label in fill-review.

{
  "format": "label-containd-ingress",
  "version": 1,
  "data": [
    {
      "title": "Vintage Leica M3",
      "productCode": "4007817326004",
      "location": "Archive · Shelf B",
      "tagList": ["camera", "vintage", "leica"],
      "fullDeepLink": "https://example.com/items/leica-m3"
    },
    {
      "title": "Hasselblad 500C/M",
      "productCode": "4007817326011",
      "location": "Archive · Shelf C",
      "tagList": ["camera", "medium-format"],
      "fullDeepLink": "https://example.com/items/hasselblad-500"
    }
  ],
  "command": {
    "action": "fill_and_show",
    "templateId": "sticker-qr-title-01-template",
    "mappingId": "default"
  }
}
Containd handoff with auto-print
{
  "format": "label-containd-ingress",
  "version": 1,
  "senderId": "containd",
  "returnDeepLink": "containd://item/YOUR_ITEM_ID",
  "data": {
    "title": "Storage Box A-12",
    "productCode": "4007817326004",
    "fullDeepLink": "https://example.com/item/box-a12"
  },
  "command": {
    "action": "fill_and_print",
    "templateId": "containd-strip-label",
    "mappingId": "containd-default",
    "options": { "autoPrint": true }
  }
}

Image, EXIF, and file import

Besides links, you can send item data as a JSON file or hidden inside a JPEG photo.

Quick comparison

You want to…Use
Test once by handPaste JSON in the app
Send a file from your computer or cloud.json file import
Share a photo that also carries item dataJPEG with embedded JSON
Short link in SMS or QRFlat URL (single item only)
Box contents or many itemsJSON file, paste, or base64 link

Paste JSON (in the app)

  1. Home → Import item data.
  2. Paste your JSON.
  3. Confirm. The app opens fill-review or asks you to pick a template.

DIY: JSON file step by step

Create a plain text file you can email, sync, or open from the Files app.

  1. Create a new file named something like my-item.json.
  2. Start with the required wrapper (see example below).
  3. Put your item fields inside data. Use one object for one label, or an array for several.
  4. Add command if you want a specific template or auto-print. If you omit it, the app shows preview with the default mapping.
  5. Validate: must be valid JSON (double quotes, no trailing commas). Online JSON validators help.
  6. Save the file with UTF-8 encoding.
  7. In Link2Label: Home → Import item data → choose file picker → select your .json.
  8. If templateId in the command is missing or unknown, pick a template when prompted.
Starter JSON file (single item)

Save this as widget.json and import it to test.

{
  "format": "label-containd-ingress",
  "version": 1,
  "data": {
    "title": "Blue storage bin",
    "description": "Winter clothes",
    "productCode": "BIN-042",
    "location": "Attic shelf 2",
    "fullDeepLink": "https://example.com/items/bin-042"
  },
  "command": {
    "action": "fill_and_show",
    "templateId": "containd-strip-label",
    "mappingId": "default"
  }
}
Starter JSON file (batch of two items)
{
  "format": "label-containd-ingress",
  "version": 1,
  "data": [
    { "title": "Item A", "productCode": "A001" },
    { "title": "Item B", "productCode": "B002" }
  ],
  "command": {
    "action": "fill_and_show",
    "mappingId": "default"
  }
}

Without templateId, the app asks you to pick a template once for the whole batch.

DIY: embed JSON in a JPEG (EXIF-style)

Link2Label scans the photo file for a JSON blob containing "format":"label-containd-ingress" (or the map format). The JSON can sit in EXIF metadata or anywhere in the JPEG bytes. This is how Containd shares a photo plus item data in one file.

Rules

  • File must be a JPEG (.jpg / .jpeg).
  • JSON must be valid and include format, version, and data.
  • The marker string must appear exactly as "format":"label-containd-ingress" (no spaces after the colon).
  • Keep the JSON under 64 KB. Larger payloads are ignored.
  • If no JSON is found, the app treats the image as a normal photo label instead.

Step by step (using ExifTool)

  1. Install ExifTool on your computer.
  2. Write your payload to a file, e.g. payload.json (use the starter examples above).
  3. Pick a JPEG photo (the map image for map labels, or any photo for standard labels).
  4. Embed the JSON into the photo's UserComment field:
exiftool -overwrite_original -UserComment<=payload.json myphoto.jpg
  1. Transfer myphoto.jpg to your phone (AirDrop, email, cloud, etc.).
  2. In Link2Label: Home → Import image → select the photo.
  3. If JSON was found, labels are filled. For map ingress, confirm the image when prompted.
  4. Review in fill-review, then print or save.
Payload file for EXIF embed (copy into payload.json)
{
  "format": "label-containd-ingress",
  "version": 1,
  "data": {
    "title": "Tool drawer",
    "productCode": "DRW-07",
    "fullDeepLink": "https://example.com/items/drw-07"
  },
  "command": {
    "action": "fill_and_show",
    "templateId": "sticker-qr-title-01-template",
    "mappingId": "default"
  }
}

Without ExifTool

Any method that places the raw JSON string inside the JPEG file can work, as long as the format marker is present and the JSON is complete. ExifTool is the most reliable option for manual testing. App developers typically embed the string in an EXIF or APP segment when exporting from their own app.

Plain photo (no JSON)

If you import an image with no embedded data, Link2Label opens the photo print flow: a simple label with your picture, not auto-filled fields.

Share sheet (native app only)

From another app, share text (JSON or a link) to Link2Label. The share extension parses it the same way as paste or tap. Requires a store build, not Expo Go.

Mapping reference

Mappings tell each placeholder where to get its value when item data arrives. They are stored inside the template file.

Mapping structure in a template file
"mappings": [
  {
    "mappingId": "default",
    "entries": [
      { "placeholderId": "text-title", "sourceKey": "title" },
      { "placeholderId": "text-code", "sourceKey": "productCode" },
      { "placeholderId": "qr", "sourceKey": "fullDeepLink" }
    ]
  }
]

Map templates may also set mapTemplate.legendPlaceholderId for the legend list.

Common source keys

Source keyGoes to
title, descriptionText placeholders
contentTree / list placeholder only
contentNumberText (item count)
fullDeepLinkQR or barcode
productCode, location, …Any text field
cat:categoryId:propertyIdCustom fields from arbitraryFieldsList
@nowDate placeholders (today's date at fill time)

Custom category fields

If item data includes:

"arbitraryFieldsList": [
  { "categoryId": "equipment", "propertyId": "serial_number", "value": "826492" }
]

Map it with source key cat:equipment:serial_number.

Multiple mappings

One template can have several mapping profiles (different mappingId values). The incoming JSON picks one via command.mappingId.

Editing in the app

Open a template → mapping editor → add rows pairing each placeholder with a source key. Placeholders you leave unmapped keep their default sample text.