This guide explains how to integrate the Classy Embedded SDK into Single Page Applications built with React, Vue, Angular, or other SPA frameworks.
Add the ?manual-init=true query parameter to the SDK script URL:
<!-- In your index.html -->
<script async src="https://embed.classy.org/api/sdk/js/YOUR_ORG_ID?manual-init=true"></script>Call eg.init() when the donation page mounts. The SDK discovers campaigns from DOM elements with data-classy-campaign attributes:
// SDK discovers campaigns from DOM - no campaignId needed in init()
await window.eg.init();
// Then open modal for any campaign on the page
window.eg.modal.open('YOUR_CAMPAIGN_ID');Call eg.destroy() when leaving the donation page:
window.eg.destroy();All methods are available on window.eg:
| Method | Returns | Description |
|---|---|---|
eg.init(config?) |
Promise<void> |
Initialize the SDK. Returns when initialization is complete. |
eg.destroy() |
void |
Clean up the SDK, remove event listeners, restore DOM elements. |
eg.isInitialized() |
boolean |
Check if the SDK is currently initialized. |
eg.modal |
ModalSDK |
Modal API for opening/closing donation modal (available after init). |
eg.sendAnalyticsEvent(name, data) |
void |
Send custom analytics events (available after init). |
import { useEffect } from 'react';
// campaignId is used for the DOM element, not for eg.init()
function DonatePage({ campaignId }) {
useEffect(() => {
let mounted = true;
const initSDK = async () => {
if (window.eg?.init && mounted) {
// SDK discovers campaigns from DOM elements
await window.eg.init();
}
};
initSDK();
// Cleanup on unmount
return () => {
mounted = false;
window.eg?.destroy();
};
}, []); // No campaignId dependency - SDK handles it
return (
<div>
<h1>Make a Donation</h1>
{/* SDK discovers this element by data-classy-campaign attribute */}
<div id="donate-grid" data-classy-campaign={campaignId}></div>
</div>
);
}<script setup>
import { onMounted, onUnmounted } from 'vue';
// campaignId is used for the DOM element, not for eg.init()
const props = defineProps(['campaignId']);
onMounted(async () => {
if (window.eg?.init) {
// SDK discovers campaigns from DOM elements
await window.eg.init();
}
});
onUnmounted(() => {
window.eg?.destroy();
});
</script>
<template>
<div>
<h1>Make a Donation</h1>
<!-- SDK discovers this element by data-classy-campaign attribute -->
<div id="donate-grid" :data-classy-campaign="campaignId"></div>
</div>
</template>import { Component, Input, OnInit, OnDestroy } from '@angular/core';
declare global {
interface Window { eg: any; }
}
@Component({
selector: 'app-donate',
// SDK discovers element by data-classy-campaign attribute
template: `
<div>
<h1>Make a Donation</h1>
<div id="donate-grid" [attr.data-classy-campaign]="campaignId"></div>
</div>
`
})
export class DonateComponent implements OnInit, OnDestroy {
// campaignId is used for the DOM element, not for eg.init()
@Input() campaignId: string = '';
async ngOnInit() {
if (window.eg?.init) {
// SDK discovers campaigns from DOM elements
await window.eg.init();
}
}
ngOnDestroy() {
window.eg?.destroy();
}
}'use client';
import { useEffect } from 'react';
// campaignId is used for the DOM element, not for eg.init()
export default function DonatePage({ campaignId }) {
useEffect(() => {
let mounted = true;
const initSDK = async () => {
// Wait for SDK to load
const waitForSDK = () => new Promise((resolve) => {
if (window.eg?.init) return resolve(window.eg);
const interval = setInterval(() => {
if (window.eg?.init) {
clearInterval(interval);
resolve(window.eg);
}
}, 100);
});
const eg = await waitForSDK();
if (mounted) {
// SDK discovers campaigns from DOM elements
await eg.init();
}
};
initSDK();
return () => {
mounted = false;
window.eg?.destroy();
};
}, []); // No campaignId dependency - SDK handles it
return (
<div>
<h1>Make a Donation</h1>
{/* SDK discovers this element by data-classy-campaign attribute */}
<div id="donate-grid" data-classy-campaign={campaignId}></div>
</div>
);
}'use client' directive since the SDK interacts with the DOM and window object.
id attribute must match a component ID from your campaign configuration. You can find this ID in the Classy Studio campaign settings.
eg.destroy() when leaving the donation pageawait when calling eg.init() - it returns a Promiseeg.init() multiple times without calling eg.destroy() first?manual-init=true to the SDK script URLMake sure you're calling eg.destroy() when leaving the page. The SDK restores the original DOM element on destroy, which allows it to be reinitialized on the next visit.
Ensure the SDK is fully initialized before the user can interact with the donation form. Use the Promise returned by init() to know when it's ready.
The SDK script loads asynchronously. Wait for it to load before calling init(). See the Next.js example for a pattern to wait for the SDK.