Template Components

Image Support Guide

Comprehensive guide to image rendering options in REFRACT PDF, including standalone images, image grids, and inline images in tables and cards. Supports both SharePoint asset paths and Base64 data URIs.

This guide covers all three image rendering options available in REFRACT PDF: standalone images, image grids, and inline images in tables and cards. All image nodes support both SharePoint asset paths and direct Base64 data URIs.

Overview

REFRACT PDF provides flexible image support for:

  • Single images with sizing and alignment control
  • Image grids for layouts like hazard pictograms or classification icons
  • Inline images in table columns and card fields
  • Conditional visibility with true or false logic, or data-driven conditions
  • Dual source formats - SharePoint assets or Base64 data URIs

Core Concepts

Image Source Format

Every image supports two source formats:

{
  "src": "Hazard/pictograms/toxic.png",
  "library": "assets"
}

How it works:

  • Fetches from SharePoint/assets/Hazard/pictograms/toxic.png
  • Automatically converted to Base64 during PDF generation
  • Supports nested paths: "Folder/SubFolder/image.png"
  • Default library: "assets" (can be overridden to "media", "images", etc.)

2. Direct Base64 Data URI

{
  "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEA..."
}

How it works:

  • Already Base64-encoded
  • No asset fetch needed - embeds directly
  • Useful for dynamically generated images (QR codes, charts) or pre-encoded backend data
  • No library selection needed
  • Best practice: Provide in data payload, NOT in template definition
  • Template should reference a data field: "src": "{{qrCodeData}}"
  • Backend pre-encodes and supplies the Base64 in the data

Conditional Visibility

All image nodes support conditional visibility:

{
  "visible": true,                    // Always visible
  "visible": false,                   // Always hidden
  "visible": "{{showHazards}}"        // Data-driven: depends on payload
}

A string visible is read as a single {{path}} token and looked up directly against the payload (getByPath) - it does not evaluate a JavaScript expression. "{{showImages && user.role === 'admin'}}" is treated as one (invalid) path rather than a boolean expression, so it always resolves to nothing and the image is always hidden. Use a single boolean field from the payload, computed upstream if the condition is more than one field.


Option 1: Standalone Image Block

Single image with optional title, sizing, and alignment.

Basic Configuration

{
  "type": "image",
  "props": {
    "title": "Toxic Substance Classification",
    "src": "Hazard/pictograms/toxic.png",
    "library": "assets",
    "width": 120,
    "height": 120,
    "alignment": "center",
    "visible": true
  }
}

Properties

PropertyTypeDefaultDescription
titlestring-Optional label above the image
srcstring-Required - Asset path or Base64 data URI
librarystring"assets"SharePoint library name (ignored for Base64)
widthnumber100Image width in points
heightnumber100Image height in points
alignment'left' | 'center' | 'right'"center"Horizontal alignment
visibleboolean or stringtrueConditional visibility

Examples

Example 1: Asset-Based Image from SharePoint

{
  "type": "image",
  "props": {
    "title": "Product Label",
    "src": "ProductImages/label_2024.png",
    "library": "assets",
    "width": 200,
    "height": 150,
    "alignment": "center"
  }
}

Example 2: Base64 Data URI

{
  "type": "image",
  "props": {
    "title": "Generated QR Code",
    "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
    "width": 150,
    "height": 150,
    "alignment": "center"
  }
}

Example 3: Conditional Visibility

{
  "type": "image",
  "props": {
    "title": "Company Logo",
    "src": "Branding/logo_full.png",
    "library": "assets",
    "width": 180,
    "height": 80,
    "alignment": "center",
    "visible": "{{isOfficialDocument}}"
  }
}

Render logic:

  • If isOfficialDocument === true in payload → image displays
  • If isOfficialDocument === false → image hidden
  • If isOfficialDocument undefined → defaults to hidden

Example 4: Nested Asset Folder

{
  "type": "image",
  "props": {
    "title": "Import Certificate",
    "src": "Compliance/Certifications/2024/ISO-9001.png",
    "library": "media",
    "width": 250,
    "height": 300,
    "alignment": "left"
  }
}

Option 2: Image Grid Block

Display multiple images in a grid layout. Perfect for hazard pictograms, classification icons, or image galleries.

Basic Configuration

{
  "type": "image_grid",
  "props": {
    "title": "Hazard Classification System",
    "columnsPerRow": 5,
    "spacing": 12,
    "visible": true,
    "images": [
      {
        "src": "Hazard/pictograms/toxic.png",
        "library": "assets",
        "label": "Toxic",
        "width": 60,
        "height": 60
      },
      {
        "src": "Hazard/pictograms/flammable.png",
        "library": "assets",
        "label": "Flammable",
        "width": 60,
        "height": 60
      },
      {
        "src": "Hazard/pictograms/oxidizer.png",
        "library": "assets",
        "label": "Oxidizer",
        "width": 60,
        "height": 60
      }
    ]
  }
}

Grid Properties

PropertyTypeDefaultDescription
titlestring-Optional title above the grid
imagesarray[]The images to render - see Image Properties below. Must be nested inside props, not a sibling of it.
columnsPerRownumber5Number of images per row
spacingnumber10Spacing between images in points
visibleboolean or stringtrueConditional visibility for entire grid

There is no grid-level library default - each image resolves its own library, defaulting to "assets" per image when omitted. Setting library once at the grid level and expecting every image to inherit it does not work.

Image Properties

Each image in the images array:

PropertyTypeDefaultDescription
srcstring-Required - Asset path or Base64 URI
librarystring"assets"SharePoint library for this image's asset path. Set on every image individually - there is no grid-level fallback.
labelstring-Optional label below the image
widthnumber60Image width in points
heightnumber60Image height in points

Examples

Example 1: Hazard Pictograms (Like Your Shipping Report)

{
  "type": "image_grid",
  "props": {
    "title": "GHS Hazard Classifications",
    "columnsPerRow": 5,
    "spacing": 15,
    "visible": "{{includeHazardInfo}}",
    "images": [
      { "src": "GHS/pictograms/acute-toxicity.png", "library": "assets", "label": "Acute Toxicity", "width": 70, "height": 70 },
      { "src": "GHS/pictograms/carcinogenicity.png", "library": "assets", "label": "Carcinogenicity", "width": 70, "height": 70 },
      { "src": "GHS/pictograms/environment-hazard.png", "library": "assets", "label": "Environmental Hazard", "width": 70, "height": 70 },
      { "src": "GHS/pictograms/exclamation-mark.png", "library": "assets", "label": "Irritant", "width": 70, "height": 70 },
      { "src": "GHS/pictograms/flame.png", "library": "assets", "label": "Flammable", "width": 70, "height": 70 },
      { "src": "GHS/pictograms/exploding-bomb.png", "library": "assets", "label": "Explosive", "width": 70, "height": 70 },
      { "src": "GHS/pictograms/flame-circle.png", "library": "assets", "label": "Oxidizing", "width": 70, "height": 70 },
      { "src": "GHS/pictograms/gas-cylinder.png", "library": "assets", "label": "Compressed Gas", "width": 70, "height": 70 },
      { "src": "GHS/pictograms/corrosion.png", "library": "assets", "label": "Corrosive", "width": 70, "height": 70 }
    ]
  }
}

Example 2: Product Classification with Mix of Sources

{
  "type": "image_grid",
  "props": {
    "title": "Product Classifications",
    "columnsPerRow": 4,
    "spacing": 10,
    "images": [
      { "src": "Categories/food.png", "library": "assets", "label": "Food", "width": 50, "height": 50 },
      { "src": "data:image/png;base64,iVBORw0KGgoAAAANS...", "label": "Electronics", "width": 50, "height": 50 },
      { "src": "Categories/pharma.png", "library": "assets", "label": "Pharmaceutical", "width": 50, "height": 50 },
      { "src": "Categories/chemical.png", "library": "assets", "label": "Chemical", "width": 50, "height": 50 }
    ]
  }
}
{
  "type": "image_grid",
  "props": {
    "title": "Document Attachments",
    "columnsPerRow": 3,
    "spacing": 20,
    "visible": "{{hasAttachments}}",
    "images": [
      { "src": "Attachments/invoice_scan.png", "library": "assets", "label": "Invoice", "width": 80, "height": 100 },
      { "src": "Attachments/receipt.png", "library": "assets", "label": "Receipt", "width": 80, "height": 100 },
      { "src": "Attachments/packing_slip.png", "library": "assets", "label": "Packing Slip", "width": 80, "height": 100 }
    ]
  }
}

"visible": "{{attachmentCount > 0}}" in the previous version of this example would not work - visible reads a single {{path}} token as-is, it does not evaluate a comparison. Compute a boolean field (hasAttachments, as above) in the payload instead.


Inline Images in Tables & Cards

imageField, imageLibrary, imageWidth, imageHeight and imageAlign on a table column or card field are defined as a TypeScript interface in hierarchyRulesEngine.ts, but nothing in the renderer reads them - setting them on a column or field definition has no effect, and the cell renders its raw text value instead of an image.

There is currently no way to render an image inline inside a table or card cell from a per-row data field. To show an image alongside tabular or card data, place a standalone image node next to the table or card (for example, in a layout_group), or use an image_grid for a small fixed set of images.


Real-World Example: Shipping Report with Hazard Pictograms

Complete document combining all image types:

{
  "type": "section",
  "props": { "title": "Shipping Report" },
  "children": [
    {
      "type": "note_block",
      "props": {
        "variant": "info",
        "label": "Hazardous Materials Notice",
        "text": "This shipment contains materials classified as hazardous. All items are packaged and labeled according to GHS regulations."
      }
    },
    {
      "type": "image",
      "props": {
        "title": "Third Coast Terminals Logo",
        "src": "Branding/terminals_logo.png",
        "library": "assets",
        "width": 200,
        "height": 80,
        "alignment": "center"
      }
    },
    {
      "type": "table",
      "props": {
        "title": "Shipment Contents",
        "dataPath": "shipmentLines",
        "columns": [
          { "key": "product", "label": "Product" },
          { "key": "quantity", "label": "Quantity (Gal)", "format": "number" }
        ]
      }
    },
    {
      "type": "image_grid",
      "props": {
        "title": "GHS Hazard Classifications Applicable to This Shipment",
        "columnsPerRow": 5,
        "spacing": 15,
        "visible": "{{includeHazardPictograms}}",
        "images": [
          { "src": "GHS/acute-toxicity.png", "library": "assets", "label": "Acute Toxicity" },
          { "src": "GHS/carcinogenicity.png", "library": "assets", "label": "Carcinogenicity" },
          { "src": "GHS/flame.png", "library": "assets", "label": "Flammable" },
          { "src": "GHS/environment.png", "library": "assets", "label": "Environmental" },
          { "src": "GHS/corrosion.png", "library": "assets", "label": "Corrosive" }
        ]
      }
    },
    {
      "type": "card",
      "props": {
        "title": "Handling Instructions",
        "fields": [
          { "key": "instructions", "label": "Instructions", "value": "{{handlingInstructions}}" }
        ]
      }
    }
  ]
}

The "hazard icon per row" column from earlier drafts of this example is not possible today - see Inline Images in Tables & Cards above. The hazard pictograms are shown once, in the image_grid block, rather than per table row.


Asset Path Reference

Standard Folders

Your SharePoint assets library likely has this structure:

SharePoint/assets/
├── Branding/
│   ├── logo_full.png
│   ├── logo_small.png
│   └── watermark.png
├── Hazard/
│   ├── pictograms/
│   │   ├── toxic.png
│   │   ├── flammable.png
│   │   ├── corrosion.png
│   │   └── ...
│   └── classifications/
│       └── ...
├── Products/
│   ├── package_front.png
│   ├── label.png
│   └── ...
├── GHS/
│   ├── acute-toxicity.png
│   ├── carcinogenicity.png
│   ├── flame.png
│   └── ...
└── Compliance/
    ├── Certifications/
    │   └── 2024/
    │       ├── ISO-9001.png
    │       └── ...
    └── Approvals/
        └── ...

Path Format Examples

DescriptionPath
Direct in assets folder"logo.png"
Subfolder"Branding/logo_full.png"
Nested folder"Hazard/pictograms/toxic.png"
Date-based folder"Compliance/Certifications/2024/ISO-9001.png"

Troubleshooting

Image Not Displaying

Problem: Image does not appear in PDF

Solutions:

  1. Asset path incorrect - Verify path exists in SharePoint: assets/Hazard/pictograms/toxic.png. A literal (non-token) path that cannot be fetched fails the render with an error naming the node, the path and the library, rather than rendering silently without the image - check the render error message first.
  2. Base64 format invalid - Ensure data URI starts with data:image/png;base64,
  3. File permissions - Check that the asset is readable by the service
  4. Visibility hidden - Check visible property or conditional logic
  5. Token src not resolving - a {{path}} src that never resolves to a usable value is dropped silently (the image is simply omitted, no error) - this is different from case 1 above

Image Distorted or Sized Wrong

Problem: Image appears stretched, squashed, or tiny

Solutions:

  1. Width or height mismatch - Use correct aspect ratio (for example, 100×100 for square images)
  2. Oversized container - Reduce width or height values for smaller displayed size
  3. Grid sizing - Adjust columnsPerRow and spacing for proper grid layout

Grid Images Not Aligned

Problem: Images in grid do not align properly

Solutions:

  1. Inconsistent image sizes - Use same width and height for all images in grid
  2. Column count too high - Reduce columnsPerRow if images are too small
  3. Spacing too large - Decrease spacing value

Asset Library Error

Problem: "Library not found" or "Asset fetch failed"

Solutions:

  1. Wrong library name - Use correct library: "assets", "media", "images"
  2. Library not enabled - Check SharePoint configuration allows access
  3. Path has spaces - URL-encode spaces: "My Documents" becomes "My%20Documents"

Performance Considerations

Base64 vs. Asset Paths

AspectAsset PathBase64 URI
Template size✅ Smaller (reference only)❌ Larger (full data embedded)
Final PDF sizeSmaller (asset fetched at runtime)Larger (~33% encoding overhead)
Generation timeSlightly slower (fetch + convert)Immediate (pre-encoded)
Best forStatic images, template size mattersDynamic or generated images, pre-encoded data
Where to useIn templateIn data payload
ExampleTemplate: "src": "Hazard/toxic.png"Data: {"hazardIcon": "data:image/png;base64,iVB..."}

Recommendations

Asset Paths (Recommended):

  • Keep templates small and reusable
  • Use for static images (logos, pictograms, classifications)
  • Define in template, fetch at document generation time
  • Example: "src": "Hazard/pictograms/toxic.png"

Base64 URIs (For Dynamic Content):

  • Use for dynamically generated images (QR codes, charts, generated reports)
  • Provide in data payload, NOT hardcoded in template
  • Pre-encoded in backend before passing to template
  • Do not embed large Base64 strings directly in .rpkg files
  • Example: Pass from data: {"qrCode": "data:image/png;base64,..."}

Performance Tips

  • Prefer asset paths in templates to keep template files small
  • Pre-generate Base64 in backend rather than encoding in template logic
  • Resize images before uploading - a smaller source image reduces both fetch time and final PDF size; there is no documented size threshold to target, size images to the space they will occupy

Available Icons

When using images in tables with conditional styling, you can combine images with icons. Here are the available icons:

  • Status Icons: check, circlealert, warning, clock, status, x
  • Info Icons: info, tag, star
  • Content Icons: file, package, list, image
  • System Icons: building, shield, barcode, phone, email, link
  • Percent: percent

See Icon Reference for the complete registry. There is no checkbox-checked, checkbox-unchecked, radio-checked, radio-unchecked or file-text icon.

For example, in conditional styles:

{
  "value": "HAZARD",
  "icon": "circlealert",
  "color": "#D32F2F"
}

On this page