Search
Loading...
Skip to content

Create From Scratch

Build reusable design templates entirely through code using CE.SDK’s programmatic APIs for automation, batch generation, and custom template creation tools.

10 mins
estimated time
Download
StackBlitz
GitHub

CE.SDK provides a complete API for building design templates through code. Instead of starting from an existing template, you can create a blank scene, define page dimensions, add text and graphic blocks, configure placeholders for swappable media, add text variables for dynamic content, apply editing constraints to protect layout integrity, and save the template for reuse. This approach enables automation workflows, batch template generation, and integration with custom template creation tools.

This guide covers how to create a blank scene, add text blocks with variables, add image placeholders, apply editing constraints, and save the template.

Initialize CE.SDK#

We start by initializing CE.SDK’s Node.js engine for server-side rendering and scene manipulation.

const engine = await CreativeEngine.init({
// license: process.env.CESDK_LICENSE
});

Create a Blank Scene#

We create the foundation of our template with custom page dimensions. The engine.scene.create() method accepts page options to set width, height, and background color.

// Template layout constants for a promotional card
const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 1000;
const PADDING = 40;
const CONTENT_WIDTH = CANVAS_WIDTH - PADDING * 2;
// Create a blank scene with custom dimensions
engine.scene.create('Free', {
page: { size: { width: CANVAS_WIDTH, height: CANVAS_HEIGHT } }
});
// Set design unit to Pixel for precise coordinate mapping
engine.scene.setDesignUnit('Pixel');

The scene creation method accepts a layout mode ('Free' for design mode) and optional page configuration. When options are provided, the scene automatically includes a page with the specified dimensions.

Set Page Background#

We set a light background color to give the template a consistent base appearance.

// Set a gradient background for the template
const backgroundFill = engine.block.createFill('gradient/linear');
engine.block.setGradientColorStops(backgroundFill, 'fill/gradient/colors', [
{ color: { r: 0.4, g: 0.2, b: 0.6, a: 1.0 }, stop: 0 }, // Purple
{ color: { r: 0.2, g: 0.4, b: 0.8, a: 1.0 }, stop: 1 } // Blue
]);
engine.block.setFill(page, backgroundFill);

We create a color fill using engine.block.createFill('color'), set the color via engine.block.setColor() with the fill/color/value property, then assign the fill to the page using engine.block.setFill().

Add Text Blocks#

Text blocks allow you to add styled text content. We create a headline that includes a variable token for dynamic content.

// Create headline text block with {{title}} variable
const headline = engine.block.create('text');
engine.block.replaceText(headline, '{{title}}');
// Set font with proper typeface for consistent rendering
engine.block.setFont(headline, FONT_BOLD, {
name: 'Roboto',
fonts: [{ uri: FONT_BOLD, subFamily: 'Bold', weight: 'bold' }]
});
engine.block.setFloat(headline, 'text/fontSize', 28);
engine.block.setTextColor(headline, { r: 1.0, g: 1.0, b: 1.0, a: 1.0 });
// Position and size the headline
engine.block.setWidthMode(headline, 'Absolute');
engine.block.setHeightMode(headline, 'Auto');
engine.block.setWidth(headline, CONTENT_WIDTH);
engine.block.setPositionX(headline, PADDING);
engine.block.setPositionY(headline, 50);
engine.block.setEnum(headline, 'text/horizontalAlignment', 'Center');
engine.block.appendChild(page, headline);

We create a text block using engine.block.create('text'), set its content with engine.block.replaceText(), configure dimensions and position, and append it to the page using engine.block.appendChild().

Add Text Variables#

Text variables enable data-driven personalization. By using {{variableName}} tokens in text blocks, you can populate content programmatically.

// Set default value for the title variable
engine.variable.setString('title', 'Summer Sale');

The engine.variable.setString() method sets the default value for the variable. When the template is used, this value can be changed to personalize the content.

Add Graphic Blocks#

Graphic blocks serve as containers for images. We create an image block that will become a placeholder for swappable media.

// Create image placeholder in the center of the card
const imageBlock = engine.block.create('graphic');
const imageShape = engine.block.createShape('rect');
engine.block.setShape(imageBlock, imageShape);
const imageFill = engine.block.createFill('image');
engine.block.setString(
imageFill,
'fill/image/imageFileURI',
'https://img.ly/static/ubq_samples/sample_1.jpg'
);
engine.block.setFill(imageBlock, imageFill);
engine.block.setWidth(imageBlock, CONTENT_WIDTH);
engine.block.setHeight(imageBlock, 420);
engine.block.setPositionX(imageBlock, PADDING);
engine.block.setPositionY(imageBlock, 295);
engine.block.appendChild(page, imageBlock);

We create a graphic block with engine.block.create('graphic'), assign a rectangle shape using engine.block.createShape('rect') and engine.block.setShape(), create an image fill with engine.block.createFill('image'), set the image URI via engine.block.setString(), and position it on the page.

Configure Placeholders#

Placeholders turn design blocks into drop-zones where users can swap content while maintaining layout integrity. We enable placeholder behavior on the image fill and configure visual controls.

// Enable placeholder behavior on the image fill
const fill = engine.block.getFill(imageBlock);
if (fill !== null && engine.block.supportsPlaceholderBehavior(fill)) {
engine.block.setPlaceholderBehaviorEnabled(fill, true);
}
engine.block.setPlaceholderEnabled(imageBlock, true);
// Enable visual controls for the placeholder
engine.block.setPlaceholderControlsOverlayEnabled(imageBlock, true);
engine.block.setPlaceholderControlsButtonEnabled(imageBlock, true);

Placeholder behavior is enabled on the fill (not the block) for graphic blocks. We also enable the overlay pattern and replace button for visual guidance.

Apply Editing Constraints#

Editing constraints protect template elements by restricting what users can modify. We use scopes to lock position and size while allowing content changes.

// Set global scope to 'Defer' for per-block control
engine.editor.setGlobalScope('layer/move', 'Defer');
engine.editor.setGlobalScope('layer/resize', 'Defer');
// Lock all text block positions but allow text editing
const textBlocks = [headline, subheadline, cta];
textBlocks.forEach((block) => {
engine.block.setScopeEnabled(block, 'layer/move', false);
engine.block.setScopeEnabled(block, 'layer/resize', false);
});
// Lock image position but allow fill replacement
engine.block.setScopeEnabled(imageBlock, 'layer/move', false);
engine.block.setScopeEnabled(imageBlock, 'layer/resize', false);
engine.block.setScopeEnabled(imageBlock, 'fill/change', true);

Setting global scope to 'Defer' enables per-block control. We then disable movement and resizing for both blocks while enabling fill changes for the image placeholder.

Save the Template#

We persist the template in two formats: a lightweight string for CDN-hosted assets and a self-contained archive with embedded assets.

// Prompt utility for interactive save options
function prompt(question: string): Promise<string> {
const rl = createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve) => {
rl.question(question, (answer) => {
rl.close();
resolve(answer);
});
});
}
async function main() {
// Display save options menu
console.log('=== Template Save Options ===\n');
console.log('1. Save as string (for CDN-hosted assets)');
console.log('2. Save as archive (self-contained ZIP)');
console.log('3. Export as PNG image');
console.log('4. Save all formats and export png\n');
const choice = (await prompt('Select save option (1-4): ')) || '4';

The engine.scene.saveToString() method creates a compact string format suitable for storage when assets are hosted externally. The engine.scene.saveToArchive() method creates a ZIP bundle containing all assets, ideal for offline use or distribution.

Cleanup#

We dispose of the engine to release resources when processing is complete.

engine.dispose();

Troubleshooting#

  • Blocks not appearing: Verify that engine.block.appendChild() attaches blocks to the page. Blocks must be part of the scene hierarchy to render.
  • Variables not resolving: Verify the variable name in the text matches exactly, including curly braces syntax {{variableName}}.
  • Placeholder not interactive: Ensure engine.block.setPlaceholderEnabled() is called on the block and the appropriate scope (fill/change) is enabled.
  • Constraints not enforced: Verify engine.editor.setGlobalScope() is set to 'Defer' before setting per-block scopes.

API Reference#

MethodDescription
engine.scene.create()Create a new design scene with optional page size
engine.scene.setDesignUnit()Set the design unit (Pixel, Millimeter, Inch)
engine.scene.saveToString()Save scene to string format
engine.scene.saveToArchive()Save scene to ZIP archive
engine.block.create()Create a design block (page, text, graphic)
engine.block.appendChild()Append a child block to a parent
engine.block.findByType()Find blocks by their type
engine.block.createFill()Create a fill (color, image, etc.)
engine.block.setFill()Assign a fill to a block
engine.block.getFill()Get the fill of a block
engine.block.createShape()Create a shape (rect, ellipse, etc.)
engine.block.setShape()Assign a shape to a graphic block
engine.block.setString()Set a string property on a block
engine.block.setColor()Set a color property
engine.block.replaceText()Set text content
engine.block.setFont()Set font with typeface
engine.block.setPlaceholderBehaviorEnabled()Enable placeholder behavior on fill
engine.block.setPlaceholderEnabled()Enable placeholder interaction on block
engine.block.setPlaceholderControlsOverlayEnabled()Enable overlay visual control
engine.block.setPlaceholderControlsButtonEnabled()Enable button visual control
engine.variable.setString()Set a text variable value
engine.editor.setGlobalScope()Set global scope permission
engine.block.setScopeEnabled()Enable/disable scope on a block

Next Steps#