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}
.
<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% scaleclone
(true|false) – create a clone instead of editing original templatelayers
– 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 previewallow-layer-resize
(true|false) – allow resizing layers in previewallow-layer-unlock
(true|false) – allow unlocking locked layersallow-layer-rename
(true|false) – allow renaming layersallow-text-edition
(true|false) – allow double-click text editing in preview
Template Permissions
allow-rename
(true|false) – allow renaming the templateallow-save
(true|false) – enable save functionalityallow-download
(true|false) – enable download functionalityallow-resize
(true|false) – allow resizing template dimensionsallow-create-template
(true|false) – enable creating new templates
UI Customization
hide-sidebar
(true|false) – hide the left sidebar panelhide-header
(true|false) – hide the top header barhide-layers-panel
(true|false) – hide the layers panelhide-language-toggle
(true|false) – hide the language switcher
Integration Options
webhook-url
(string) – override default webhook URL for this sessionexternal-id
(string) – session identifier for persistent uploads, fonts, and content taggingmove-to-folder
(string) – automatically move saved templates to folder IDfolder
(string) – limit template selection to specific folder IDimage-url
(string) – URL of image to load as background or layerw
(number) – custom template width in pixelsh
(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
// Update layer valuesiframe.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 capabilitiesiframe.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 capabilitiesiframe.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 iframeiframe.contentWindow.postMessage({ type: 'LOAD_TEMPLATE', templateId: 'tpl_123', clone: false }, '*');
// Save the current templateiframe.contentWindow.postMessage({ type: 'SAVE' }, '*');
// Download the templateiframe.contentWindow.postMessage({ type: 'DOWNLOAD' }, '*');
Events the editor sends back
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; }});
Recommended template switching strategy
For best performance and reliable initialization:
- Load the first template using the URL (
/editor/preview/{templateId}?embed=...
) so the editor initializes correctly. - Switch to other templates using the
LOAD_TEMPLATE
message to avoid iframe reloads and keep caches warm.
Complete 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>