React Hook
For Mini Apps built with React (TanStack Start, Next.js, or plain Vite React), wrap the global window.AdsKinz object in a hook instead of calling it directly in components — the controller reference stays stable across re-renders and cleans up correctly.
useAdsKinz
typescript
import { useCallback, useEffect, useRef } from "react";
interface UseAdsKinzOptions {
kinzId: string;
format: "interstitial" | "popInterstitial";
onError?: (error: { code: string }) => void;
}
export function useAdsKinz({ kinzId, format, onError }: UseAdsKinzOptions) {
const controllerRef = useRef<any>(undefined);
useEffect(() => {
if (typeof window === "undefined" || !window.AdsKinz) return;
controllerRef.current = window.AdsKinz.init({ kinzId, format });
}, [kinzId, format]);
const showAd = useCallback(async () => {
if (!controllerRef.current) {
onError?.({ code: "NOT_INITIALIZED" });
return;
}
try {
return await controllerRef.current.show();
} catch (error) {
onError?.(error as { code: string });
}
}, [onError]);
return showAd;
}Usage in a component
tsx
function UnlockButton() {
const showAd = useAdsKinz({
kinzId: "kz_live_9f3a2c1b",
format: "interstitial",
onError: (err) => console.log("Ad skipped or unavailable:", err.code),
});
return (
<button
onClick={async () => {
await showAd();
unlockFeature(); // called whether the ad completed or was closed
}}
>
Continue
</button>
);
}Never gate strictly on completed
Especially for
popInterstitial, "closed" is the normal outcome. Let the user continue either way.Loading the script in a React app
The SDK ships as a script tag, not an npm package. Add it once in your root document or head component instead of injecting it per component:
html
<script src="https://competent-rebekkah-davelam-58380a6b.koyeb.app/sdk/kinz.js" defer></script>With server-side rendering (TanStack Start, Next.js), guard any direct reference to window.AdsKinz behind a typeof window !== "undefined" check, as the hook above already does — the SDK object does not exist during server rendering.