1. Introduction
SKULabs templates use LiquidJS — a powerful templating language that gives you full control over the layout and content of your printable documents (packing slips, invoices, pick lists, and more).
Why LiquidJS templates are better than V1:
Live preview — See changes instantly as you type in the editor
AI-powered Template Assistant — Describe what you want in plain English and the assistant writes the code for you
Custom tags — Purpose-built tags for tables, barcodes, layouts, store logos, and more
More flexible — Full support for conditionals, loops, filters, and HTML/CSS styling
New accounts use LiquidJS templates by default. If you're on an older account, you can switch from the template editor.
2. Getting Started
Navigate to Store > Templates
Select a template type (e.g., Packing Slip)
The editor opens with two panels:
Left side — Code editor where you write your template
Right side — Live preview that updates as you type
3. Understanding Variables
Variables are data fields available in your template. They represent real order data (customer name, SKU, price, etc.) that get filled in when the template renders.
Syntax:
Output a value:
{{ variableName }}Nested access:
{{ customer.name }},{{ origin.address }}
4. How to View Available Variables (Data Model)
If you're not sure what variables are available or what path to use:
In the template editor, click the three-dot menu (next to the Save button)
Select "View data model"
A dialog opens showing the complete JSON structure of sample data
Browse the tree to find the exact variable path you need
Use this as your reference when writing template code
For example, if you see customer > name in the data model, use {{ customer.name }} in your template.
5. Using Liquid Syntax
Output a value
{{ orderNumber }} {{ customer.name }}Conditionals
{% if customer.company %} {{ customer.company }} {% elsif customer.name %} {{ customer.name }} {% else %} No customer info {% endif %}Loops
{% for item in lines %} {{ item.name }} - {{ item.sku }} {% endfor %}Assign a variable
{% assign myVar = "Hello" %} {% assign shippedItems = 0 %}Default filter
Use default to provide a fallback when a value is empty:
{{ origin.company | default: origin.name }}6. Custom Filters
formatDate
Formats a date value using format tokens.
{{ currentDate | formatDate: 'Mon DD YYYY' }} {{ orderDate | formatDate: 'MM/DD/YYYY' }} {{ newShipments[0].time | formatDate: 'Mon DD YYYY HH:mm' }}
{{ currentDate | date: "%a %b %d %H:%M" }}
Available tokens:
Token | Output | Example |
| Four-digit year | 2025 |
| Two-digit year | 25 |
| Abbreviated month | Jan, Feb, Aug |
| Month, 2 digits | 01, 08, 12 |
| Month, no leading zero | 1, 8, 12 |
| Day, 2 digits | 01, 15, 31 |
| Day, no leading zero | 1, 15, 31 |
| Hours, 2 digits | 00, 09, 23 |
| Hours, no leading zero | 0, 9, 23 |
| Minutes, 2 digits | 00, 05, 59 |
| Minutes, no leading zero | 0, 5, 59 |
| Seconds, 2 digits | 00, 05, 59 |
| Seconds, no leading zero | 0, 5, 59 |
%a | Abbreviated weekday | Mon, Tue, Wed |
%b | Abbreviated month | Jan, Feb, Mar |
%d | Day of the month (zero-padded) | 01-31 |
%H | Hour (24-hour format) | 00-23 |
%M | Minutes | 00-59 |
currency
Formats a number as currency.
{{ item.price | currency: 'USD' }} {{ orderSummary.total | currency: orderSummary.currency }}where_exp
Filters a collection based on an expression. Useful for showing only certain items.
{% assign shippedLines = lines | where_exp: "item", "item.shipped > 0" %} {% assign unshippedLines = lines | where_exp: "item", "item.remaining > 0" %}7. Custom Tags (SKULabs-Specific)
These are special tags built specifically for SKULabs templates. They handle layout, tables, barcodes, and other common document elements.
PackingSlip — Document wrapper
Wraps the entire packing slip content. Required as the outermost tag.
{% PackingSlip %} ... your template content ... {% endPackingSlip %}Options:
Custom page size:
{% PackingSlip width: 4in; height: 6in %}Landscape orientation:
{% PackingSlip landscape %}
FlexRow / FlexColumn — Layout
Create horizontal rows with columns inside them. Use this to place content side by side.
{% FlexRow %} {% FlexColumn %} Left-side content {% endFlexColumn %} {% FlexColumn text-right %} Right-aligned content {% endFlexColumn %} {% endFlexRow %}FlexColumn options: text-left, text-right, text-center
Table — Dynamic data table
Renders a table by iterating over a collection (like lines).
{% Table item in lines %}
{% Headers[Name, SKU, Ordered, Shipped] %}
{% Column %}
{{ item.name }}
{% Column %}
{{ item.sku }}
{% Column %}
{{ item.ordered }}
{% Column %}
{{ item.shipped }}
{% KitItems %}
{% endTable %}Table options:
Filter:
{% Table item in lines filter: item.shipped > 0 %}— only show rows matching the conditionSort:
{% Table item in lines sort: item.name %}or{% Table item in lines sort: item.price:desc %}Offset:
{% Table item in lines offset: 2 %}— skip the first 2 itemsLimit:
{% Table item in lines limit: 5 %}— show only 5 items
You can combine options: {% Table item in lines filter: item.remaining > 0 sort: item.name limit: 10 %}
Headers — Table header row
Defines the column headers for a Table. Place inside a Table tag.
{% Headers[Product, SKU, Qty, Price] %}Column — Table column content
Separates column content inside a Table. Each {% Column %} starts a new column. Supports inline styles:
{% Column style="text-align: center" %}Barcode — Generate a barcode
Renders a barcode from a variable value.
{% Barcode orderNumber %} {% Barcode item.barcode width: 2; height: 30 %} {% Barcode orderNumber show_text: true; font_size: 20 %}Options (separated by semicolons):
width— Bar width (default: 2)height— Bar height in pixels (default: 25)show_text— Show the value as text below the barcode (true/false)font_size— Text font size whenshow_textis truescale— Scale factor (default: 1)
StoreLogo — Render store logo
Renders the store's logo image. If no logo is set, it renders the store name as a heading instead.
{% StoreLogo %}Bin — Bin and batch number
Renders the bin number and batch/group number in a bordered box. Only renders if both values exist.
{% Bin %}KitItems — Kit sub-items
Place inside a Table tag. If the current line item is a kit, this renders a sub-table showing the kit's component items with their name, SKU, ordered, and shipped quantities.
{% KitItems %}TrackingButton — Tracking link
Renders a clickable "Track package" button that links to the shipment's tracking URL.
{% TrackingButton newShipments[0] %}8. Template Assistant (AI)
The Template Assistant is located at the top of the editor. It lets you modify your template using natural language — just describe what you want.
How to use it
Type your request in the assistant input at the top of the editor
The assistant modifies your template code and you see the result in the live preview
If you don't like the change, type "undo" or "revert last" to go back one step
Follow-up questions work — the assistant remembers the conversation context
Tips for effective prompts
Be specific about variable names. The assistant knows the data model, so referencing exact field names gets better results.
Prompt | Why it works |
"Add | Specific variable + clear placement |
"Show | Specific variable + relative position |
"Add a column showing | Specific variable + formatting |
"Hide lines where | Specific condition using a real field |
Prompt | Why it's less effective |
"Add the product code" | Ambiguous — could be SKU, barcode, or lineId |
"Make it look better" | Too vague — what specifically should change? |
How to find the right variable name
Open View data model from the three-dot menu
Browse the JSON tree to find the field you want
Use the exact path in your prompt (e.g.,
item.namingDetails.variant)
9. Walkthrough: Default Packing Slip Template
Section-by-section breakdown
1. Document wrapper
{% PackingSlip %} ... {% endPackingSlip %} — Wraps everything. Sets up the page with default letter size (8.5" x 11").
2. Header row
{% FlexRow %} creates a two-column header:
Left column: Store logo (
{% StoreLogo %}), current date formatted as "Mon DD YYYY", and the order number prefixed with#Right column (right-aligned): A barcode of the order number with visible text, plus the bin/batch number box
3. Address row
Another {% FlexRow %} with two columns:
Left: Store/origin info — company name (falling back to warehouse name via
| default:), phone, email, and full address. Each field is wrapped in{% if %}so it only shows when present.Right: Customer info — company, name (only if different from company), phone, email, and full address.
4. Shipment details
Two lines showing the shipped date (from the first shipment in newShipments) and the requested shipping method.
5. Products table
{% Table item in lines %} iterates over all order line items with four columns: Name, SKU, Ordered, and Shipped. The {% KitItems %} tag at the end adds a sub-table for kit components when applicable.
6. Customer notes
Shows customer notes at the bottom, but only if the value is not empty (using {% if customerNotes != '' %}).
10. Tips and Best Practices
Always check the live preview as you edit — it updates in real time so you'll catch issues immediately
Use "View data model" to find the exact variable path before writing template code
Use conditionals to handle missing data — wrap optional fields in
{% if %}blocks so they don't show blank lines:{% if customer.phone %}Phone: {{ customer.phone }}<br>{% endif %}The Template Assistant works best with specific variable names — open the data model first, find the field path, then use it in your prompt
Use
| default:to provide fallback values:{{ origin.company | default: origin.name }}Use
| currency:for prices to get proper formatting:{{ item.price | currency: 'USD' }}Use
| formatDate:for dates instead of showing raw timestamps:{{ orderDate | formatDate: 'Mon DD, YYYY' }}Combine Table options to show exactly the data you need:
{% Table item in lines filter: item.remaining > 0 sort: item.name %}
