Skip to content

Preview Mode

The Preview Mode is a canvas-only version of the editor designed for fast, distraction-free embedding. It hides the editor UI and focuses on rendering and manipulating template content. You can configure the initial state via URL parameters and control behavior at runtime with postMessage.

You can try a demo implementation here.

Enable Preview Mode

Use the preview base path: /editor/preview/{TEMPLATE_ID}?embed={CONFIG_ID}.

Basic Preview Embed
<iframe
id="template-embed"
src="https://app.templated.io/editor/preview/{$TEMPLATE_ID}?embed={$CONFIG_ID}"
width="100%"
height="600"
frameborder="0"
></iframe>

Optional URL parameters

Basic Configuration

  • zoom (10–100) – initial zoom level; 50 equals 100% scale
  • clone (true|false) – create a clone instead of editing original template
  • layers – base64-encoded JSON with initial layer data (see Advanced page)
  • metadata – base64-encoded JSON with custom metadata for webhooks

Layer Permissions

  • allow-layer-move (true|false) – allow moving layers in preview
  • allow-layer-resize (true|false) – allow resizing layers in preview
  • allow-layer-unlock (true|false) – allow unlocking locked layers
  • allow-layer-rename (true|false) – allow renaming layers
  • allow-text-edition (true|false) – allow double-click text editing in preview

Template Permissions

  • allow-rename (true|false) – allow renaming the template
  • allow-save (true|false) – enable save functionality
  • allow-download (true|false) – enable download functionality
  • allow-resize (true|false) – allow resizing template dimensions
  • allow-create-template (true|false) – enable creating new templates

UI Customization

  • hide-sidebar (true|false) – hide the left sidebar panel
  • hide-header (true|false) – hide the top header bar
  • hide-layers-panel (true|false) – hide the layers panel
  • hide-language-toggle (true|false) – hide the language switcher

Integration Options

  • webhook-url (string) – override default webhook URL for this session
  • external-id (string) – session identifier for persistent uploads, fonts, and content tagging
  • move-to-folder (string) – automatically move saved templates to folder ID
  • folder (string) – limit template selection to specific folder ID
  • image-url (string) – URL of image to load as background or layer
  • w (number) – custom template width in pixels
  • h (number) – custom template height in pixels

Example with flags:

<iframe
src="https://app.templated.io/editor/preview/{$TEMPLATE_ID}?embed={$CONFIG_ID}
&zoom=50&allow-layer-move=true&allow-layer-resize=true"
width="100%" height="600" frameborder="0"
></iframe>

Runtime control (postMessage)

After the iframe loads, you can control Preview Mode without reloading using postMessage. The editor will also send status events back.

Messages you can send to the editor

Parent → Editor
// Update layer values
iframe.contentWindow.postMessage({
type: 'UPDATE_LAYERS',
data: {
'headline': { text: 'New title', color: '#FF0000' },
'hero-image': { image_url: 'https://example.com/image.jpg' },
'background': { fill: '#0066CC' }
}
}, '*');
// Set zoom (10–100; 50 = 100% scale)
iframe.contentWindow.postMessage({ type: 'SET_ZOOM', zoom: 60 }, '*');
// Toggle layer capabilities
iframe.contentWindow.postMessage({ type: 'SET_ALLOW_LAYER_MOVE', allowLayerMove: true }, '*');
iframe.contentWindow.postMessage({ type: 'SET_ALLOW_LAYER_RESIZE', allowLayerResize: true }, '*');
iframe.contentWindow.postMessage({ type: 'SET_ALLOW_LAYER_UNLOCK', allowLayerUnlock: true }, '*');
iframe.contentWindow.postMessage({ type: 'SET_ALLOW_LAYER_RENAME', allowLayerRename: true }, '*');
iframe.contentWindow.postMessage({ type: 'SET_ALLOW_TEXT_EDITION', allowTextEdition: true }, '*');
// Toggle template capabilities
iframe.contentWindow.postMessage({ type: 'SET_ALLOW_RENAME', allowRename: true }, '*');
iframe.contentWindow.postMessage({ type: 'SET_ALLOW_SAVE', allowSave: true }, '*');
iframe.contentWindow.postMessage({ type: 'SET_ALLOW_DOWNLOAD', allowDownload: true }, '*');
iframe.contentWindow.postMessage({ type: 'SET_ALLOW_RESIZE', allowResize: true }, '*');
iframe.contentWindow.postMessage({ type: 'SET_ALLOW_CREATE_TEMPLATE', allowCreateTemplate: true }, '*');
// Load a different template without reloading the iframe
iframe.contentWindow.postMessage({ type: 'LOAD_TEMPLATE', templateId: 'tpl_123', clone: false }, '*');
// Save the current template
iframe.contentWindow.postMessage({ type: 'SAVE' }, '*');
// Download the template
iframe.contentWindow.postMessage({ type: 'DOWNLOAD' }, '*');

Events the editor sends back

Editor → Parent
window.addEventListener('message', (event) => {
// Optional: verify origin: if (event.origin !== 'https://app.templated.io') return;
const msg = event.data;
switch (msg?.type) {
// Editor lifecycle
case 'EDITOR_READY':
// Editor initialized; safe to send UPDATE_LAYERS / SET_* messages
break;
// Template events
case 'TEMPLATE_LOADED':
// URL-based initial load completed; contains template details
console.log('Loaded via URL:', msg.template);
break;
case 'TEMPLATE_LOADED_SUCCESS':
// Successful LOAD_TEMPLATE postMessage
console.log('Template switched:', msg.templateId);
break;
case 'TEMPLATE_LOAD_ERROR':
console.error('Template load failed:', msg.error);
break;
case 'TEMPLATE_SAVED_SUCCESS':
console.log('Template saved:', msg.templateId);
break;
case 'TEMPLATE_SAVE_ERROR':
console.error('Template save failed:', msg.error);
break;
// Layer events
case 'LAYERS_UPDATED':
// Acknowledges UPDATE_LAYERS
break;
case 'LAYER_UPDATE_ERROR':
console.error('Layer update failed:', msg.error);
break;
// Zoom events
case 'ZOOM_UPDATED':
console.log('Zoom now:', msg.zoom); // same 10–100 scale where 50 = 100%
break;
case 'ZOOM_UPDATE_ERROR':
console.error('Zoom update failed:', msg.error);
break;
// Layer capability events
case 'ALLOW_LAYER_MOVE_UPDATED':
console.log('Layer move permission:', msg.allowLayerMove);
break;
case 'ALLOW_LAYER_RESIZE_UPDATED':
console.log('Layer resize permission:', msg.allowLayerResize);
break;
case 'ALLOW_LAYER_UNLOCK_UPDATED':
console.log('Layer unlock permission:', msg.allowLayerUnlock);
break;
case 'ALLOW_LAYER_RENAME_UPDATED':
console.log('Layer rename permission:', msg.allowLayerRename);
break;
case 'ALLOW_TEXT_EDITION_UPDATED':
console.log('Text edition permission:', msg.allowTextEdition);
break;
// Template capability events
case 'ALLOW_RENAME_UPDATED':
console.log('Rename permission:', msg.allowRename);
break;
case 'ALLOW_SAVE_UPDATED':
console.log('Save permission:', msg.allowSave);
break;
case 'ALLOW_DOWNLOAD_UPDATED':
console.log('Download permission:', msg.allowDownload);
break;
case 'ALLOW_RESIZE_UPDATED':
console.log('Resize permission:', msg.allowResize);
break;
case 'ALLOW_CREATE_TEMPLATE_UPDATED':
console.log('Create template permission:', msg.allowCreateTemplate);
break;
// Error events
case 'ALLOW_LAYER_MOVE_UPDATE_ERROR':
case 'ALLOW_LAYER_RESIZE_UPDATE_ERROR':
case 'ALLOW_LAYER_UNLOCK_UPDATE_ERROR':
case 'ALLOW_LAYER_RENAME_UPDATE_ERROR':
case 'ALLOW_TEXT_EDITION_UPDATE_ERROR':
case 'ALLOW_RENAME_UPDATE_ERROR':
case 'ALLOW_SAVE_UPDATE_ERROR':
case 'ALLOW_DOWNLOAD_UPDATE_ERROR':
case 'ALLOW_RESIZE_UPDATE_ERROR':
case 'ALLOW_CREATE_TEMPLATE_UPDATE_ERROR':
console.error('Permission update failed:', msg.error);
break;
}
});

For best performance and reliable initialization:

  1. Load the first template using the URL (/editor/preview/{templateId}?embed=...) so the editor initializes correctly.
  2. Switch to other templates using the LOAD_TEMPLATE message to avoid iframe reloads and keep caches warm.

Complete example

Comprehensive working example
<iframe id="template-embed" width="100%" height="700" frameborder="0"></iframe>
<div>
<button onclick="toggleLayerMove()">Toggle Layer Move</button>
<button onclick="toggleLayerResize()">Toggle Layer Resize</button>
<button onclick="toggleTextEditing()">Toggle Text Editing</button>
<button onclick="updateContent()">Update Content</button>
<button onclick="saveTemplate()">Save Template</button>
</div>
<script>
const iframe = document.getElementById('template-embed');
const CONFIG_ID = 'YOUR_EMBED_CONFIG_ID';
const FIRST_TEMPLATE = 'tpl_ABC123';
let layerMoveEnabled = false;
let layerResizeEnabled = false;
let textEditingEnabled = false;
// 1) Initial URL-based load with multiple flags
const url = new URL(`https://app.templated.io/editor/preview/${FIRST_TEMPLATE}`);
url.searchParams.set('embed', CONFIG_ID);
url.searchParams.set('zoom', '50'); // 50 = 100% scale
url.searchParams.set('allow-layer-move', 'true');
url.searchParams.set('allow-text-edition', 'true');
url.searchParams.set('hide-sidebar', 'true');
url.searchParams.set('clone', 'true'); // Work with a clone
url.searchParams.set('external-id', 'user-demo-session'); // Persistent session
iframe.src = url.toString();
// 2) Listen for editor events
window.addEventListener('message', (event) => {
const msg = event.data;
console.log('Received message:', msg);
switch (msg?.type) {
case 'EDITOR_READY':
console.log('✅ Editor ready for interactions');
// Initialize with some content
updateContent();
break;
case 'TEMPLATE_LOADED':
console.log('📄 Template loaded:', msg.template);
break;
case 'TEMPLATE_SAVED_SUCCESS':
console.log('💾 Template saved successfully:', msg.templateId);
alert('Template saved successfully!');
break;
case 'TEMPLATE_SAVE_ERROR':
console.error('❌ Save failed:', msg.error);
alert('Failed to save template: ' + msg.error);
break;
case 'LAYERS_UPDATED':
console.log('✅ Layers updated successfully');
break;
case 'ALLOW_LAYER_MOVE_UPDATED':
layerMoveEnabled = msg.allowLayerMove;
console.log('🚀 Layer move:', layerMoveEnabled ? 'enabled' : 'disabled');
break;
case 'ALLOW_LAYER_RESIZE_UPDATED':
layerResizeEnabled = msg.allowLayerResize;
console.log('🔄 Layer resize:', layerResizeEnabled ? 'enabled' : 'disabled');
break;
case 'ALLOW_TEXT_EDITION_UPDATED':
textEditingEnabled = msg.allowTextEdition;
console.log('✏️ Text editing:', textEditingEnabled ? 'enabled' : 'disabled');
break;
}
});
// 3) Control functions
function toggleLayerMove() {
layerMoveEnabled = !layerMoveEnabled;
iframe.contentWindow.postMessage({
type: 'SET_ALLOW_LAYER_MOVE',
allowLayerMove: layerMoveEnabled
}, '*');
}
function toggleLayerResize() {
layerResizeEnabled = !layerResizeEnabled;
iframe.contentWindow.postMessage({
type: 'SET_ALLOW_LAYER_RESIZE',
allowLayerResize: layerResizeEnabled
}, '*');
}
function toggleTextEditing() {
textEditingEnabled = !textEditingEnabled;
iframe.contentWindow.postMessage({
type: 'SET_ALLOW_TEXT_EDITION',
allowTextEdition: textEditingEnabled
}, '*');
}
function updateContent() {
iframe.contentWindow.postMessage({
type: 'UPDATE_LAYERS',
data: {
'headline': {
text: 'Updated at ' + new Date().toLocaleTimeString(),
color: '#FF6B35'
},
'description': {
text: 'This content was updated via postMessage API',
color: '#333333'
},
'background': {
fill: '#F0F8FF'
}
}
}, '*');
}
function saveTemplate() {
iframe.contentWindow.postMessage({ type: 'SAVE' }, '*');
}
</script>

See also