Skip to main content

React

CheckoutWidget.tsx
import { useEffect, useRef } from 'react';

declare global {
interface Window {
HelaMesh?: { mount: (target: any, opts: any) => { destroy: () => void } };
}
}

export function CheckoutWidget({ invoiceId }: { invoiceId: string }) {
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
let instance: { destroy: () => void } | null = null;

const tryMount = () => {
if (!ref.current || !window.HelaMesh) return false;
instance = window.HelaMesh.mount(ref.current, {
invoiceId,
publishableKey: process.env.NEXT_PUBLIC_HELAMESH_PK!,
onPaid: (event) => {
// Update your local state, redirect, fulfil order
window.location.href = `/order/success?inv=${event.invoice.id}`;
},
});
return true;
};

if (!tryMount()) {
// SDK not loaded yet โ€” wait for it
const script = document.createElement('script');
script.src = 'https://pay.helamesh.com/sdk/v1.js';
script.async = true;
script.onload = tryMount;
document.body.appendChild(script);
}

return () => instance?.destroy();
}, [invoiceId]);

return <div ref={ref} />;
}

The component is idempotent โ€” tryMount checks if the SDK is already loaded (e.g., if another component loaded it first). The destroy() cleanup runs on unmount so nothing leaks.