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:
1. SharePoint Asset Path (Recommended)
{
"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
| Property | Type | Default | Description |
|---|---|---|---|
title | string | - | Optional label above the image |
src | string | - | Required - Asset path or Base64 data URI |
library | string | "assets" | SharePoint library name (ignored for Base64) |
width | number | 100 | Image width in points |
height | number | 100 | Image height in points |
alignment | 'left' | 'center' | 'right' | "center" | Horizontal alignment |
visible | boolean or string | true | Conditional 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 === truein payload → image displays - If
isOfficialDocument === false→ image hidden - If
isOfficialDocumentundefined → 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
| Property | Type | Default | Description |
|---|---|---|---|
title | string | - | Optional title above the grid |
images | array | [] | The images to render - see Image Properties below. Must be nested inside props, not a sibling of it. |
columnsPerRow | number | 5 | Number of images per row |
spacing | number | 10 | Spacing between images in points |
visible | boolean or string | true | Conditional 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:
| Property | Type | Default | Description |
|---|---|---|---|
src | string | - | Required - Asset path or Base64 URI |
library | string | "assets" | SharePoint library for this image's asset path. Set on every image individually - there is no grid-level fallback. |
label | string | - | Optional label below the image |
width | number | 60 | Image width in points |
height | number | 60 | Image 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 }
]
}
}Example 3: Thumbnail Gallery
{
"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
| Description | Path |
|---|---|
| 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:
- 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. - Base64 format invalid - Ensure data URI starts with
data:image/png;base64, - File permissions - Check that the asset is readable by the service
- Visibility hidden - Check
visibleproperty or conditional logic - 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:
- Width or height mismatch - Use correct aspect ratio (for example, 100×100 for square images)
- Oversized container - Reduce width or height values for smaller displayed size
- Grid sizing - Adjust
columnsPerRowandspacingfor proper grid layout
Grid Images Not Aligned
Problem: Images in grid do not align properly
Solutions:
- Inconsistent image sizes - Use same width and height for all images in grid
- Column count too high - Reduce
columnsPerRowif images are too small - Spacing too large - Decrease
spacingvalue
Asset Library Error
Problem: "Library not found" or "Asset fetch failed"
Solutions:
- Wrong library name - Use correct library:
"assets","media","images" - Library not enabled - Check SharePoint configuration allows access
- Path has spaces - URL-encode spaces:
"My Documents"becomes"My%20Documents"
Performance Considerations
Base64 vs. Asset Paths
| Aspect | Asset Path | Base64 URI |
|---|---|---|
| Template size | ✅ Smaller (reference only) | ❌ Larger (full data embedded) |
| Final PDF size | Smaller (asset fetched at runtime) | Larger (~33% encoding overhead) |
| Generation time | Slightly slower (fetch + convert) | Immediate (pre-encoded) |
| Best for | Static images, template size matters | Dynamic or generated images, pre-encoded data |
| Where to use | In template | In data payload |
| Example | Template: "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
.rpkgfiles - 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"
}Conditional Visibility & Layout Collapse
How props.visible and expressions.visibleWhen hide a node, when to use each one, and how a layout_group recalculates column widths when a child is hidden.
PDF Assets
Embed PDF pages as inline images or append full PDF documents to a REFRACT PDF output. Two modes (EMBED and APPEND) cover different use cases from extracting a single chart to attaching a complete technical manual. PDF assets work in standalone structured content blocks and inside supplementary annex image grids.