Widget Documentation
Everything you need to embed and customize the <image-layer> widget for image, video, voice, and text workflows in your application.
Quick Start
Add the widget to any HTML page after your backend creates an end-user session token:
<script type="module" src="https://cdn.imagelayer.app/widget/latest/image-layer.js"></script>
<image-layer id="imagelayer" api-url="https://api.imagelayer.app"></image-layer>
<script>
const widget = document.getElementById('imagelayer');
widget.apiKey = 'il_pub_your_publishable_key_here';
widget.sessionToken = 'SESSION_TOKEN_FROM_YOUR_SERVER';
</script> The widget renders as a self-contained UI. Users choose a supported workflow, generate on-brand output, and stay inside your product the whole time. Keep live API keys on trusted servers where possible; use the session token to scope each end user.
Installation
CDN rolling path
Use latest for demos, previews, and quick starts. The bytes can change as new widget releases roll out.
<script type="module" src="https://cdn.imagelayer.app/widget/latest/image-layer.js"></script> Pinned CDN path (recommended for production)
Pin a versioned or sha-* path and include Subresource Integrity when you need reproducible rollbacks or stricter supply-chain controls.
<script
type="module"
src="https://cdn.imagelayer.app/widget/<version>/image-layer.js"
integrity="sha384-<hash>"
crossorigin="anonymous"
></script> Self-hosted
Copy the built widget files to your static assets and reference the local path:
<script type="module" src="/assets/image-layer.js"></script> Attributes
All configuration is done via HTML attributes on the <image-layer> element.
| Attribute | Type | Default | Description |
|---|---|---|---|
| apiKey | property string | "" | Publishable widget key assigned as a JavaScript property. Do not place secret keys in HTML attributes. |
| api-url | string | "" | Required. Base URL of the ImageLayer API. |
| sessionToken | property string | "" | JWT from POST /v1/auth/session. Assign it as a JavaScript property for live generation flows. |
| authToken | property string | "" | Organization auth token used by dashboard and playground flows. Use together with sessionToken. |
| theme | "light" | "dark" | "auto" | "auto" | Color scheme. "auto" follows the system light/dark preference; "dark" forces dark palette. |
| show-model-selector | boolean | true | Show the model dropdown for the image, video, audio, or text options available in the current widget configuration. |
| demo-mode | boolean | false | Enable the limited hosted demo experience for previews and local testing. |
| demo-config | string (JSON) | undefined | JSON configuration for demo mode presets, visible modes, locked-mode tooltips, and interactive preview behavior. |
| brand-guidelines | boolean | false | Enables the brand guidelines toggle in the widget UI. |
| brand-guidelines-data | string (JSON) | undefined | JSON object with brand guidelines configuration (colors, tone, style rules). |
| animation-style | "spring" | "smooth" | "none" | "spring" | Controls the animation style for UI transitions. |
demo-config also supports preview-specific keys such as
visible_output_modes,
disabled_output_modes_reason,
preview_behavior,
preview_gate_message,
preview_cta_href,
preview_cta_label, and
history_preview_items.
Events
The widget emits custom events that bubble up through the DOM. Listen with addEventListener.
| Event | Fires when | event.detail |
|---|---|---|
| il:generate | User submits a prompt or selects a history item. | { prompt: string, output_mode: string, content_type?: string, seed?: number } |
| il:submit | User finalizes the branded image in the image flow. | { url: string, blob: Blob, generation_id?: string, seed?: number, prompt_metadata?: object } |
| il:video-generated | Video generation completes. | { url: string, duration?: number } |
| il:audio-generated | Audio generation completes. | { url: string, voice?: string, duration?: number, format?: string } |
| il:voiceover-generated | A video voiceover finishes. | { id: string, video_url: string, audio_url?: string, voice?: string, duration?: number } |
| il:verification-required | The hosted demo needs an external verification step. | { prompt: string, output_mode: string } |
| il:error | An error occurs during generation. | { error: string, error_code?: string } |
| il:text-generated | AI text generation completes. | { text: string } |
| il:quota-exceeded | The user exceeds their credit quota. | { current: number, limit: number, plan: string, canPurchaseCredits: boolean } |
const widget = document.querySelector('image-layer');
widget.addEventListener('il:generate', (e) => {
console.log('Generating:', e.detail.prompt);
});
widget.addEventListener('il:submit', (e) => {
console.log('Final image URL:', e.detail.url);
// e.detail.blob contains the raw Blob for upload
});
widget.addEventListener('il:video-generated', (e) => {
console.log('Video ready:', e.detail.url);
});
widget.addEventListener('il:audio-generated', (e) => {
console.log('Audio ready:', e.detail.url);
});
widget.addEventListener('il:error', (e) => {
console.error('Widget error:', e.detail.error);
}); Theming
The widget uses CSS custom properties for full visual customization.
Override them on the <image-layer> element to match your brand.
image-layer {
/* Brand colors */
--il-primary: #6366F1;
--il-primary-hover: #4F46E5;
--il-accent: #10B981;
--il-accent-hover: #059669;
/* Backgrounds */
--il-bg: #FFFFFF;
--il-bg-secondary: #F8FAFC;
--il-bg-tertiary: #F1F5F9;
/* Text */
--il-text: #0F172A;
--il-text-secondary: #475569;
--il-text-muted: #94A3B8;
/* Borders */
--il-border: #E2E8F0;
--il-border-hover: #CBD5E1;
/* Error / Success */
--il-error: #EF4444;
--il-success: var(--il-accent);
/* Border radius */
--il-radius: 10px;
--il-radius-sm: 6px;
--il-radius-lg: 14px;
--il-radius-full: 9999px;
/* Typography */
--il-font: 'Inter', system-ui, sans-serif;
/* Shadows */
--il-shadow-xs: 0 1px 2px rgba(0,0,0,0.04);
--il-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px -1px rgba(0,0,0,0.08);
--il-shadow-md: 0 4px 6px -1px rgba(0,0,0,0.08), 0 2px 4px -2px rgba(0,0,0,0.06);
--il-shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.08), 0 4px 6px -4px rgba(0,0,0,0.04);
} Dark Theme
Set theme="dark" on the element. Override dark-specific variables:
image-layer[theme="dark"] {
--il-bg: #0F172A;
--il-text: #F1F5F9;
--il-border: #334155;
--il-primary: #818CF8;
} Server-Side Setup
For live embeds, the widget requires a session token to authenticate end users. Your server obtains this token by calling the ImageLayer API. Dashboard and playground flows also pass an auth-token.
1. Get your API key
Go to Dashboard → API Keys and create a secret key for your backend. Use a publishable key in the browser and add every site origin that will embed the widget.
2. Create a session (server-side)
Call POST /v1/auth/session from your backend:
// Run on your backend, not in the browser.
const body = JSON.stringify({
external_user_id: 'user-123',
display_name: 'Jane Doe',
});
await fetch('https://api.imagelayer.app/v1/auth/session', {
method: 'POST',
headers: {
'X-API-Key': process.env.IMAGELAYER_SECRET_KEY,
'Content-Type': 'application/json',
},
body,
}); Response:
{
"session_token": "eyJhbG...",
"expires_at": "2026-03-29T22:00:00.000Z",
"user": {
"id": "uuid",
"external_id": "user-123",
"display_name": "Jane Doe"
}
} 3. Pass the token to the widget
Assign session_token and the publishable key to the widget as JavaScript properties after fetching them from your API.
Full Integration Example
A complete HTML page with session creation and widget embedding.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My App — AI Media Generator</title>
<script type="module" src="https://cdn.imagelayer.app/widget/latest/image-layer.js"></script>
<style>
image-layer {
max-width: 480px;
margin: 40px auto;
--il-primary: #2563EB;
--il-radius-lg: 16px;
}
</style>
</head>
<body>
<image-layer
id="widget"
api-url="https://api.imagelayer.app"
theme="light"
></image-layer>
<script>
// Fetch session token from YOUR backend
async function initWidget() {
const res = await fetch('/api/imagelayer-session');
const { session_token, publishable_key } = await res.json();
const widget = document.getElementById('widget');
widget.apiKey = publishable_key;
widget.sessionToken = session_token;
}
const widget = document.getElementById('widget');
widget.addEventListener('il:submit', (e) => {
// Upload the final branded image to your storage
const formData = new FormData();
formData.append('image', e.detail.blob, 'branded-image.png');
fetch('/api/upload', { method: 'POST', body: formData });
});
initWidget();
</script>
</body>
</html> Demo Mode
For previews and local development, use demo mode to render the hosted limited demo experience:
<image-layer
api-url="https://api.imagelayer.app"
demo-mode
show-model-selector
theme="dark"
></image-layer>