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
'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.tsximport { 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}/productsAuthorization: Bearer YOUR_API_TOKENContent-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/ordersAuthorization: Bearer YOUR_API_TOKENContent-Type: application/jsonX-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