Sign Customiser works with any headless commerce setup. Embed via iframe and communicate through the postMessage API. No vendor lock-in, no styling conflicts.
Quick Start
Get a working product customiser in your headless storefront.
1. Create Your Account
Register at web.signcustomiser.com/register. Free tier available.
2. Create a Customiser
From your dashboard, click Get Started and choose a sign type (neon, acrylic, metal, etc.). Configure your pricing rules and options.
3. Get Your Embed Code
In your dashboard, navigate to your customiser settings to get your unique iframe embed code. See how to find your customiser ID.
4. Add the Embed Code
Paste the iframe into your page:
<iframe
src="https://web.signcustomiser.com/embed/YOUR_CUSTOMISER_ID"
width="100%"
height="600"
frameborder="0"
allow="clipboard-write"
id="sign-customiser-iframe"
></iframe>
Replace YOUR_CUSTOMISER_ID with the ID from your dashboard.
Handling Product Creation Events
When a customer completes their design and clicks “Add to Cart”, Sign Customiser sends a message to your page. Listen for it with the postMessage API:
window.addEventListener('message', (event) => {
// Verify the message origin for security
if (event.origin !== 'https://web.signcustomiser.com') return;
if (event.data.type === 'onProductCreated') {
const productData = event.data;
// Your custom add-to-cart logic here
console.log('Product created:', productData);
// After processing, hide the loading screen
const iframe = document.querySelector('#sign-customiser-iframe');
iframe.contentWindow.postMessage(
{ type: 'hideLoadingScreen' },
'https://web.signcustomiser.com'
);
}
});
The onProductCreated event contains all product details including dimensions, pricing, and preview images.
Framework Examples
// components/SignCustomiser.tsx
'use client';
import { useEffect, useRef } from 'react';
interface SignCustomiserProps {
customiserId: string;
onProductCreated: (data: any) => void;
}
export function SignCustomiser({ customiserId, onProductCreated }: SignCustomiserProps) {
const iframeRef = useRef<HTMLIFrameElement>(null);
// Store callback in ref to avoid re-running effect when it changes
const callbackRef = useRef(onProductCreated);
callbackRef.current = onProductCreated;
useEffect(() => {
function handleMessage(event: MessageEvent) {
// Verify the message origin for security
if (event.origin !== 'https://web.signcustomiser.com') return;
if (event.data.type === 'onProductCreated') {
callbackRef.current(event.data);
// Hide loading screen after processing
iframeRef.current?.contentWindow?.postMessage(
{ type: 'hideLoadingScreen' },
'https://web.signcustomiser.com'
);
}
}
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, []);
return (
<iframe
ref={iframeRef}
src={`https://web.signcustomiser.com/embed/${customiserId}`}
width="100%"
height="600"
frameBorder="0"
allow="clipboard-write"
/>
);
}
<template>
<iframe
ref="iframeRef"
:src="`https://web.signcustomiser.com/embed/${customiserId}`"
width="100%"
height="600"
frameborder="0"
allow="clipboard-write"
/>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const props = defineProps({
customiserId: String
});
const emit = defineEmits(['productCreated']);
const iframeRef = ref(null);
function handleMessage(event) {
// Verify the message origin for security
if (event.origin !== 'https://web.signcustomiser.com') return;
if (event.data.type === 'onProductCreated') {
emit('productCreated', event.data);
// Hide loading screen
iframeRef.value?.contentWindow?.postMessage(
{ type: 'hideLoadingScreen' },
'https://web.signcustomiser.com'
);
}
}
onMounted(() => {
window.addEventListener('message', handleMessage);
});
onUnmounted(() => {
window.removeEventListener('message', handleMessage);
});
</script>
// app/routes/products.$handle.tsx
import { useLoaderData } from '@shopify/remix-oxygen';
import { useEffect, useRef } from 'react';
export default function Product() {
const { product, customiserId } = useLoaderData();
const iframeRef = useRef<HTMLIFrameElement>(null);
useEffect(() => {
function handleMessage(event: MessageEvent) {
// Verify the message origin for security
if (event.origin !== 'https://web.signcustomiser.com') return;
if (event.data.type === 'onProductCreated') {
// Add to Hydrogen cart
console.log('Product created:', event.data);
iframeRef.current?.contentWindow?.postMessage(
{ type: 'hideLoadingScreen' },
'https://web.signcustomiser.com'
);
}
}
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, []);
return (
<div>
<h1>{product.title}</h1>
<iframe
ref={iframeRef}
src={`https://web.signcustomiser.com/embed/${customiserId}`}
width="100%"
height="600"
frameBorder="0"
allow="clipboard-write"
/>
</div>
);
}
For native Shopify integration with automatic product sync, see the Shopify app.
Custom API Integration
For advanced integrations, you can sync products and orders directly with the Sign Customiser API.
Product Sync API
When a product is created in the customiser, sync it with your backend:
POST https://web.signcustomiser.com/api/customisers/{customiserId}/products
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
{
"product_id": "your-product-id",
"cart": {},
"price_breakdown": {},
"custom_background_path": "url",
"custom_background_original_path": "url",
"product_type_paths": {},
"logo_original_path": null
}
Order Submission API
When an order is placed, notify Sign Customiser:
POST https://web.signcustomiser.com/api/orders
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
X-API-Client: generic
{
"order_id": "123456",
"order_number": "ORD-1001",
"products": [
{ "id": "product-id-from-sync" }
]
}
See the full Custom API Integration guide for webhook setup, signature verification, and complete payload schemas.
Why Headless?
Traditional product configurators cause three problems in headless architectures:
SSR conflicts. Most configurators call browser APIs on load, causing hydration mismatches.
Styling collisions. Injected CSS clashes with your design system.
Data silos. Getting product data out requires workarounds.
Sign Customiser avoids these issues. The iframe provides complete isolation, and the postMessage API gives you full control over the data flow.
What You Get
- Real-time rendering — designs update instantly as customers type
- Dynamic pricing — price recalculates on every change
- Multiple sign types — neon, LED, acrylic, metal, wood with unique rendering
- Production-ready exports — completed designs include dimensions, specs, and previews
Related
- Generic Integration Guide — step-by-step embed instructions
- Custom API Integration — webhook and API setup
- Handle Add to Cart Actions — event handling details
- Shopify App — native integration with product sync
- Website Embed — generic embed guide