Automate template workflows with CE.SDK’s engine APIs for batch processing, personalization, and headless design generation.
Templates are scenes with predefined structures that support dynamic content through variables. This guide shows you how to work with templates programmatically using CE.SDK’s engine APIs—without requiring user interface interactions.
This guide covers creating templates from scratch, configuring text variables, populating templates with data, implementing batch processing workflows, and exporting personalized designs.
Initialize CE.SDK#
We start by initializing CE.SDK and creating a design scene. This provides the foundation for programmatic template operations.
const config = { // license: process.env.CESDK_LICENSE, logger: (message: string, logLevel?: string) => { if (logLevel === 'ERROR' || logLevel === 'WARN') { console.log(`[${logLevel}]`, message); } }};
engine = await CreativeEngine.init(config);console.log('✓ Engine initialized');We create a page with specific dimensions and set a light gray background. This serves as the canvas for our template structure.
Creating Templates from Scratch#
We build templates programmatically by creating and arranging blocks with engine.block.create() and engine.block.appendChild().
// Create a greeting card template from scratchconst scene = engine.scene.create();const page = engine.block.create('page');engine.block.appendChild(scene, page);
// Set page dimensionsengine.block.setWidth(page, 800);engine.block.setHeight(page, 600);
// Set page backgroundconst pageFill = engine.block.getFill(page);engine.block.setColor(pageFill, 'fill/color/value', { r: 0.95, g: 0.95, b: 0.95, a: 1.0});
// Set up text variables FIRST so they're available when text is createdengine.variable.setString('recipientName', 'Template');engine.variable.setString('customMessage', 'This is a template example');
// Add title text block with variable placeholderconst titleBlock = engine.block.create('text');engine.block.setName(titleBlock, 'title');engine.block.appendChild(page, titleBlock);engine.block.setPositionX(titleBlock, 50);engine.block.setPositionY(titleBlock, 50);engine.block.setWidth(titleBlock, 700);engine.block.setHeight(titleBlock, 80);
// Set text with variable syntaxengine.block.replaceText(titleBlock, 'Hello, {{recipientName}}!');engine.block.setTextColor(titleBlock, { r: 0.2, g: 0.2, b: 0.2, a: 1.0});
// Configure font size and weightengine.block.setFloat(titleBlock, 'text/fontSize', 48);engine.block.setBool(titleBlock, 'text/bold', true);
// Add message text block with variableconst messageBlock = engine.block.create('text');engine.block.setName(messageBlock, 'message');engine.block.appendChild(page, messageBlock);engine.block.setPositionX(messageBlock, 50);engine.block.setPositionY(messageBlock, 140);engine.block.setWidth(messageBlock, 700);engine.block.setHeight(messageBlock, 120);
engine.block.replaceText(messageBlock, '{{customMessage}}');engine.block.setTextColor(messageBlock, { r: 0.3, g: 0.3, b: 0.3, a: 1.0});
engine.block.setFloat(messageBlock, 'text/fontSize', 28);
console.log('✓ Template structure created');We create a greeting card template with two text blocks. The title block contains {{recipientName}} and the message block contains {{customMessage}}—these double-brace syntax markers define where variables will be replaced. We position each block precisely and configure text properties like font size and color.
Text Variables for Dynamic Content#
Variables enable text replacement throughout templates. We set variable values with engine.variable.setString(), which automatically updates any text containing {{variableName}} syntax.
// Variables have already been set earlier in the template creation// List all variablesconst allVariables = engine.variable.findAll();console.log('✓ Variables initialized:', allVariables);We initialize variables for recipientName and customMessage. The engine.variable.getString() method retrieves current values, and engine.variable.findAll() lists all variables in the scene. Variables persist with the scene and automatically update text content whenever changed.
Saving Templates for Reuse#
Templates can be serialized for storage and reuse. We use engine.scene.saveToString() to create portable template files.
// Save the template for reuseconst templateString = await engine.scene.saveToString();writeFileSync('template.scene', templateString);console.log('✓ Template saved to template.scene');The saveToString() method returns a base64-encoded string containing the complete scene. This string can be stored in databases or file systems for later use in batch processing workflows.
Batch Processing with Templates#
We demonstrate batch processing by populating the template with multiple data records and exporting personalized outputs.
// Demonstrate batch processing: populate template with multiple data recordsconst recipients = [ { name: 'Alice', message: 'Congratulations on your promotion!' }, { name: 'Bob', message: 'Happy Birthday! Have a wonderful day!' }, { name: 'Charlie', message: 'Thank you for your amazing work!' }];
console.log('\n✓ Starting batch processing...');
for (let i = 0; i < recipients.length; i++) { const recipient = recipients[i];
// Populate template with recipient data engine.variable.setString('recipientName', recipient.name); engine.variable.setString('customMessage', recipient.message);
// Export the personalized card const blob = await engine.block.export(page, { mimeType: 'image/png', targetWidth: 800, targetHeight: 600 });
const filename = `greeting-card-${recipient.name.toLowerCase()}.png`; const buffer = Buffer.from(await blob.arrayBuffer()); writeFileSync(filename, buffer); console.log(` ✓ Exported ${filename}`);}
console.log('\n✓ Batch processing complete!');console.log(`✓ Generated ${recipients.length} personalized cards`);The batch processing pattern loads a template once, then iterates through data records. For each record, we update variables with specific values, then export the result. This efficient approach generates multiple personalized designs from a single template. Each export operation renders the current state to a PNG file saved to disk.
Data-Driven Workflows#
Batch processing combines template creation, data population, and export operations. A common pattern loads a template once, then iterates through data records:
const templateString = await engine.scene.saveToString();
for (const record of dataRecords) { await engine.scene.loadFromString(templateString); engine.variable.setString('name', record.name); engine.variable.setString('title', record.title);
const page = engine.block.findByType('page')[0]; const buffer = await engine.block.export(page, 'image/png'); writeFileSync(`output-${record.id}.png`, Buffer.from(buffer));}This pattern works for generating personalized certificates, greeting cards, social media graphics, or any scenario requiring multiple customized outputs from a single template.
Loading Existing Templates#
Templates can be loaded from various sources. Use engine.scene.loadFromURL() to fetch remote templates:
await engine.scene.loadFromURL( 'https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene');For templates with embedded assets, engine.scene.loadFromArchiveURL() loads the complete package including all resources.
The engine.scene.applyTemplateFromString() and engine.scene.applyTemplateFromURL() methods merge template content into existing scenes without replacing everything—useful for adding template sections to ongoing designs.
Populating Template Content#
We populate templates by updating variables with new data. When variables are updated, all text blocks containing those variable references automatically display the new values. This approach enables efficient template population without manually finding and updating individual blocks.
Managing Variables#
Variables integrate with text blocks automatically. When you set a variable value, CE.SDK updates all text containing that variable immediately:
engine.variable.setString('userName', 'Alice');// All text with {{userName}} now displays 'Alice'
engine.variable.setString('userName', 'Bob');// Same text now displays 'Bob'Remove variables with engine.variable.remove() when they’re no longer needed. This doesn’t affect existing text—the variable syntax remains as literal text.
API Reference#
| Method | Description |
|---|---|
engine.block.create() | Create a new design block |
engine.block.appendChild() | Add a block to the scene hierarchy |
engine.block.setPositionX() | Set block horizontal position |
engine.block.setPositionY() | Set block vertical position |
engine.block.setWidth() | Set block width |
engine.block.setHeight() | Set block height |
engine.block.replaceText() | Set text content (supports variable tokens) |
engine.variable.setString() | Create or update a text variable |
engine.variable.getString() | Read the current value of a variable |
engine.variable.findAll() | Get array of all variable keys in the scene |
engine.variable.remove() | Delete a variable from the scene |
engine.scene.saveToString() | Serialize scene to portable string |
engine.scene.loadFromString() | Load scene from serialized string |
engine.scene.loadFromURL() | Load scene from remote URL |
engine.block.export() | Export block to image blob |
Troubleshooting#
Template loading failures: Verify scene strings are properly encoded and URLs are accessible. Use try-catch blocks around loading operations to handle network or parsing errors.
Variables not replacing text: Variable names in text (within {{}}) must exactly match keys passed to engine.variable.setString(). Variables are case-sensitive.
Export issues: Validate that all required assets are accessible before exporting. Missing images or fonts can cause export failures. Check block hierarchy structure—orphaned blocks (not connected to the page tree) won’t appear in exports.