Tutorial: Storefront Events for Analytics and Custom Integrations
The Autopictura app embed dispatches custom events on the window object while a customer creates a design on your product page. You can listen to these events from your theme or a custom pixel to track the design journey in your analytics, show your own loading states, or sync selections with an external system. No app changes are needed; the events are dispatched by default.
Where to Add Your Code
Add your listeners somewhere that runs on the product page before or shortly after the Autopictura app embed loads, for example:
- In your theme's
layout/theme.liquid, inside a<script>tag in the<head> - In a Custom Liquid section or block on the product template
- In a Shopify custom pixel (Settings → Customer events), for analytics that must keep working across theme changes
Listeners registered before the embed loads catch every event, including autopicturaReady. Listeners added later still receive all subsequent events.
Stability
Event names and the documented payload properties are a public contract. Properties may be added, but existing ones will not be renamed or removed without a deprecation period, in which case the old form keeps working alongside the new one (see Legacy Event Names below). Only rely on the properties listed here; anything else in event.detail is internal and may change.
Quick Start
// Listen to the ready event
window.addEventListener('autopicturaReady', (event) => {
console.log('Autopictura is ready!', event.detail.sdk);
});
// Listen to design selection
window.addEventListener('autopicturaDesignSelected', (event) => {
console.log('Design selected:', event.detail);
});
Available Events
autopicturaReady
Fired once the Autopictura app embed has initialized on the product page and is ready for use.
Detail:
| Property | Type | Description |
|---|---|---|
sdk | object | The initialized Autopictura instance |
Example:
window.addEventListener('autopicturaReady', (event) => {
const { sdk } = event.detail;
// SDK is now available for programmatic access
console.log('Autopictura initialized');
});
autopicturaStateChanged
Fired whenever the overall generation state changes (selection, deselection, initialization). isInitialized tells you whether the embed has finished setting up, so you can tell "no design yet because it is still loading" apart from "no design selected".
Detail:
| Property | Type | Description |
|---|---|---|
generationSelected | boolean | Whether a generation is currently selected |
generationId | string | null | The selected generation's ID |
imageUrl | string | null | URL of the selected generated image |
designLink | string | undefined | Link to view/edit the design |
isInitialized | boolean | Whether the state has been initialized |
Example:
window.addEventListener('autopicturaStateChanged', (event) => {
const { generationSelected, generationId, imageUrl } = event.detail;
if (generationSelected) {
console.log('Generation selected:', generationId);
}
});
autopicturaVariantChanged
Fired when the selected product variant changes.
Detail:
| Property | Type | Description |
|---|---|---|
variantId | string | null | The newly selected variant ID, or null when no variant could be detected |
Example:
window.addEventListener('autopicturaVariantChanged', (event) => {
console.log('Variant changed to:', event.detail.variantId);
});
autopicturaStyleSelected
Fired when a user clicks on a style card to select it.
Detail:
| Property | Type | Description |
|---|---|---|
styleId | string | The style's unique ID |
styleName | string | The style's display name |
Example:
window.addEventListener('autopicturaStyleSelected', (event) => {
const { styleName, styleId } = event.detail;
console.log('Style selected:', styleName);
// Track in analytics
gtag('event', 'style_selected', {
style_id: styleId,
style_name: styleName
});
});
autopicturaUploadStarted
Fired when a user selects a file to upload (before cropping). The file name is deliberately not included, because customer-chosen file names can contain personal information and window events are visible to every script on the page.
Detail:
| Property | Type | Description |
|---|---|---|
fileSize | number | Size of the selected file in bytes |
Example:
window.addEventListener('autopicturaUploadStarted', (event) => {
console.log('Upload started:', `${Math.round(event.detail.fileSize / 1024)}KB`);
});
autopicturaUploadComplete
Fired when the cropped image has been uploaded, or when cropping or uploading failed. If the customer closes the crop dialog without confirming, no upload is attempted and this event does not fire.
Detail:
| Property | Type | Description |
|---|---|---|
imageId | string | null | The uploaded image's unique ID (null if failed) |
success | boolean | Whether the upload succeeded |
Example:
window.addEventListener('autopicturaUploadComplete', (event) => {
const { success, imageId } = event.detail;
if (success) {
console.log('Upload complete, image ID:', imageId);
} else {
console.log('Upload failed');
}
});
autopicturaGenerationStarted
Fired when AI generation begins (after the user clicks "Generate").
Detail:
| Property | Type | Description |
|---|---|---|
styleId | string | The style being used for generation |
styleName | string | The style's display name |
variantId | string | null | The selected variant ID, or null for catch-all products without a detected variant |
Example:
window.addEventListener('autopicturaGenerationStarted', (event) => {
const { styleName, variantId } = event.detail;
console.log('Generating with style:', styleName);
// Show loading indicator
document.body.classList.add('autopictura-generating');
});
autopicturaGenerationComplete
Fired when AI generation completes, whether it succeeded, returned no images, or failed with an error. Every autopicturaGenerationStarted is followed by exactly one autopicturaGenerationComplete.
Detail:
| Property | Type | Description |
|---|---|---|
generationId | string | null | The generation ID (null if failed) |
imageUrl | string | null | URL of the generated image (null if failed) |
success | boolean | Whether the generation was successful |
Example:
window.addEventListener('autopicturaGenerationComplete', (event) => {
const { success, generationId, imageUrl } = event.detail;
if (success) {
console.log('Generation complete:', generationId);
// Track successful generation
gtag('event', 'generation_complete', { generation_id: generationId });
} else {
console.log('Generation failed');
}
// Hide loading indicator
document.body.classList.remove('autopictura-generating');
});
autopicturaDesignSelected
Fired when a user clicks on a generated image to select it for their cart.
Detail:
| Property | Type | Description |
|---|---|---|
generationId | string | The selected generation's ID |
imageUrl | string | URL of the selected image |
designLink | string | Link to view/edit the design |
Example:
window.addEventListener('autopicturaDesignSelected', (event) => {
const { generationId, imageUrl } = event.detail;
console.log('Design selected for cart:', generationId);
// Update custom UI elements
updatePreviewImage(imageUrl);
});
autopicturaDesignDeselected
Fired when the selected design is cleared: the customer clicks "Change", switches to another variant, or deletes the selected design.
Detail: Empty object {}
Example:
window.addEventListener('autopicturaDesignDeselected', () => {
console.log('Design deselected');
// Reset custom UI elements
clearPreviewImage();
});
Legacy Event Names
Before the autopictura prefix, three of these events shipped under older names. They are still dispatched with the same payload, so existing theme code keeps working, but new integrations should use the autopictura* names.
| Current name | Legacy name |
|---|---|
autopicturaReady | aiGeneratorReady |
autopicturaStateChanged | aiGenerationStateChanged |
autopicturaVariantChanged | aiVariantIdChanged |
Common Use Cases
Analytics Tracking
// Track the full user journey
window.addEventListener('autopicturaStyleSelected', (e) => {
gtag('event', 'autopictura_style_selected', {
style_id: e.detail.styleId,
style_name: e.detail.styleName
});
});
window.addEventListener('autopicturaGenerationStarted', (e) => {
gtag('event', 'autopictura_generation_started', e.detail);
});
window.addEventListener('autopicturaGenerationComplete', (e) => {
gtag('event', 'autopictura_generation_complete', {
success: e.detail.success,
generation_id: e.detail.generationId
});
});
window.addEventListener('autopicturaDesignSelected', (e) => {
gtag('event', 'autopictura_design_selected', e.detail);
});
Custom Loading States
window.addEventListener('autopicturaGenerationStarted', () => {
document.querySelector('.custom-loader').style.display = 'block';
});
window.addEventListener('autopicturaGenerationComplete', () => {
document.querySelector('.custom-loader').style.display = 'none';
});
Sync with External Systems
window.addEventListener('autopicturaDesignSelected', async (event) => {
// Notify external system
await fetch('/api/design-selected', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event.detail)
});
});
Listening to All Events
For debugging or comprehensive tracking:
const events = [
'Ready', 'StateChanged', 'VariantChanged', 'StyleSelected',
'UploadStarted', 'UploadComplete', 'GenerationStarted',
'GenerationComplete', 'DesignSelected', 'DesignDeselected'
];
events.forEach(eventName => {
window.addEventListener(`autopictura${eventName}`, (event) => {
console.log(`autopictura${eventName}`, event.detail);
});
});