This repository demonstrates how to run the IMG.LY CreativeEngine in headless mode inside a React app. The guide below walks you through recreating the setup from scratch so you can adapt it to your own project.
- Node.js 18 or newer
- npm 9+ (bundled with Node 18)
- A valid CreativeEngine license key (replace the one in the sample)
# JavaScript template
npx create-react-app headless-demo
# or TypeScript template
npx create-react-app headless-demo --template typescriptChange into the project directory:
cd headless-demo- Replace the contents of
src/App.(js|tsx)with the component shown below. - The component imports the CreativeEngine directly from the CDN and executes the headless export inside a
useEffecthook, disposing the engine on unmount to avoid resource leaks.
import { useEffect, useState } from 'react';
import './App.css';
import CreativeEngine from 'https://cdn.img.ly/packages/imgly/cesdk-engine/1.43.0/index.js';
const config = {
license: 'YOUR_LICENSE_KEY',
baseURL: 'https://cdn.img.ly/packages/imgly/cesdk-engine/1.43.0/assets',
headless: true
};
function App() {
const [status, setStatus] = useState('Initializing headless CreativeEngine…');
useEffect(() => {
let engineInstance;
let cancelled = false;
async function createDesign() {
try {
engineInstance = await CreativeEngine.init(config);
if (cancelled) {
engineInstance.dispose();
return;
}
const scene = engineInstance.scene.create();
const page = engineInstance.block.create('page');
engineInstance.block.setWidth(page, 800);
engineInstance.block.setHeight(page, 600);
engineInstance.block.appendChild(scene, page);
const imageBlock = engineInstance.block.create('graphic');
engineInstance.block.setShape(imageBlock, engineInstance.block.createShape('rect'));
const imageFill = engineInstance.block.createFill('image');
engineInstance.block.setString(
imageFill,
'fill/image/imageFileURI',
'https://img.ly/static/ubq_samples/sample_1.jpg'
);
engineInstance.block.setFill(imageBlock, imageFill);
engineInstance.block.setFloat(imageBlock, 'position/x', 100);
engineInstance.block.setFloat(imageBlock, 'position/y', 100);
engineInstance.block.setWidth(imageBlock, 300);
engineInstance.block.setHeight(imageBlock, 300);
engineInstance.block.appendChild(page, imageBlock);
const textBlock = engineInstance.block.create('text');
engineInstance.block.setString(textBlock, 'text/text', 'Hello from Headless Mode!');
engineInstance.block.setFloat(textBlock, 'position/x', 100);
engineInstance.block.setFloat(textBlock, 'position/y', 450);
engineInstance.block.appendChild(page, textBlock);
const blob = await engineInstance.block.export(page, 'image/png');
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'headless-output.png';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
setStatus('Export complete. Check your downloads for headless-output.png.');
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
setStatus(`Engine failed: ${message}`);
}
}
createDesign();
return () => {
cancelled = true;
if (engineInstance) {
engineInstance.dispose();
}
};
}, []);
return (
<main>
<h1>Headless CreativeEngine Tutorial</h1>
<p>{status}</p>
</main>
);
}
export default App;Tip: keep the component UI minimal, because the export runs automatically when the component mounts. For custom workflows, you might trigger the export from a button click instead.
Update src/App.css to match the look-and-feel you prefer. The sample project uses a centered layout with a status panel so the runtime output is easy to read.
npm startVisit http://localhost:3000 and your browser will download headless-output.png once the engine finishes rendering.
- License errors: double-check the CreativeEngine license key in
config.license. - CORS/network issues: ensure the CDN asset URLs are reachable from your environment.
- Multiple downloads: the effect runs on every mount. If you do not want automatic exports, guard the effect behind user interaction.
Happy building!