Sample Code with React
Load SDK
Load NAM SDK in the application's index page.
<!DOCTYPE html>
<html lang="en">
<head>
<title>React App</title>
<script async src="https://ssl.pstatic.net/tveta/libs/glad/prod/gfp-core.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
The NAM SDK must be loaded only once.
To ensure this, it is recommended to load the SDK by adding the script inside <head>
tag of initial index.html
template. However, if you plan to dynamically load the SDK inside a React component, be careful to avoid loading the SDK multiple times.
SDK Invocation
When calling methods of the NAM SDK, you must use the Command Queue to ensure the SDK has been fully initialized.
In React, you can define a Custom Hook to simplify the process of calling SDK methods without needing to use the command queue every time.
- Suspense
- State
This hook delays rendering using Suspense until the SDK is initialized. It ensures the SDK's initialization state and allows you to access SDK methods in a type-safe manner.
let sdk;
window.gladsdk = (window.gladsdk || { cmd: [] });
const suspender = new Promise((resolve) => {
window.gladsdk.cmd.push(resolve);
}).then(() => {
sdk = window.gladsdk;
});
export function useGladSdk() {
if (!sdk) {
throw suspender;
}
return sdk;
}
import { Suspense } from 'react';
import { useGladSdk } from './use-glad-sdk';
const MyAd = () => {
const gladSdk = useGladSdk();
useEffect(() => {
const adSlot = gladSdk.defineAdSlot({
adUnitId: 'ad_unit_id',
adSlotElementId: 'slot',
});
gladSdk.displayAd(adSlot)
}, [gladSdk]);
return <div id="slot" />
}
const MyComponent = () => {
return (
// Fallback is rendered if the SDK is not initialized
<Suspense fallback={null}>
<MyAd />
</Suspense>
)
}
If you prefer not to use Suspense, you can define a hook using state instead. This way, you can check the SDK initialization status through state and call the SDK methods accordingly.
import { useState } from 'react';
let sdk;
const initListeners: (() => void)[] = [];
window.gladsdk = (window.gladsdk || { cmd: [] });
window.gladsdk.cmd.push(() => {
sdk = window.gladsdk;
initListeners.forEach(listener => listener());
});
export function useGladSdk() {
const [gladSdk, setGladSdk] = useState(sdk);
useEffect(() => {
if (gladSdk) return;
initListeners.push(() => {
setGladSdk(sdk);
});
}, [gladSdk])
return gladsdk;
}
import { useGladSdk } from './use-glad-sdk';
const MyComponent = () => {
const gladSdk = useGladSdk();
useEffect(() => {
if (!gladSdk) return; // undefined if the SDK is not initialized
const adSlot = gladSdk.defineAdSlot({
adUnitId: 'ad_unit_id',
adSlotElementId: 'slot',
});
gladSdk.displayAd(adSlot)
}, [gladSdk]);
return <div id="slot" />
}
All the examples below are based on useGladSdk
implemented with Suspense.
If you're using a state-based useGladSdk
, make sure you check for the existence of gladSdk
when accessing it, as shown in the examples above. To avoid mistakes, it is recommended to set the strict option in TypeScript to true
.
Define an Ad Slot
When displaying multiple ad units, you can improve code reusability by defining a Hook and component to manage the ad slots.
import { useEffect, useRef } from 'react';
import { useGladSdk } from './use-glad-sdk';
type AdSlotInfo = Record<string, any>;
type AdEventListener = (ad: any, error?: unknown) => void;
interface AdEventListeners {
onAdLoaded?: AdEventListener;
onAdClicked?: AdEventListener;
onAdImpressed?: AdEventListener;
onAdMuteCompleted?: AdEventListener;
onError?: AdEventListener;
}
const defaultListeners = {};
/**
* A Hook that creates an AdSlot for the given AdSlotInfo and adds an event listener to the AdSlot.
*/
export function useAdSlot(adSlotInfo: AdSlotInfo, eventListeners: AdEventListeners = defaultListeners) {
const gladSdk = useGladSdk();
const [adSlot, setAdSlot] = useState(() => gladSdk.defineAdSlot(adSlotInfo));
const adSlotInfoRef = useRef<AdSlotInfo>();
const eventListenersRef = useRef(eventListeners);
if (!deepEqual(adSlotInfoRef.current, adSlotInfo)) {
adSlotInfoRef.current = adSlotInfo;
}
const optimalAdSlotInfo = adSlotInfoRef.current;
useEffect(() => {
const _adSlot = gladSdk.defineAdSlot(optimalAdSlotInfo);
setAdSlot(_adSlot);
return () => {
gladSdk.destroyAdSlots([_adSlot]);
};
}, [gladSdk, optimalAdSlotInfo]);
useEffect(() => {
eventListenersRef.current = eventListeners;
}, [eventListeners]);
useEffect(() => {
const eventMap: Record<string, string> = {
[gladSdk.event.AD_LOADED]: 'onAdLoaded',
[gladSdk.event.AD_CLICKED]: 'onAdClicked',
[gladSdk.event.AD_IMPRESSED]: 'onAdImpressed',
[gladSdk.event.AD_MUTE_COMPLETED]: 'onAdMuteCompleted',
[gladSdk.event.ERROR]: 'onError',
};
const disposers = Object.entries(eventMap).map(([event, listenerName]) => {
const listener: AdEventListener = (ad, error) => {
if (ad.slot !== adSlot) return;
eventListenersRef.current[listenerName]?.(ad, error);
}
gladSdk.addEventListener(event, listener);
return () => gladSdk.removeEventListener(event, listener);
});
return () => {
disposers.forEach((dispose) => dispose());
};
}, [gladSdk, adSlot]);
return adSlot;
};
function deepEqual(a: unknown, b: unknown): a is typeof b {
// implement your deep compare method for better stability.
return JSON.stringify(a) === JSON.stringify(b)
}
import { ComponentPropsWithoutRef, ComponentRef, forwardRef } from 'react';
type NaverAdRef = ComponentRef<'div'>;
type NaverAdProps = ComponentPropsWithoutRef<'div'> & {
adSlot: any;
};
/**
* A component that takes an AdSlot as props and creates an element to display the ad.
*/
export const NaverAd = forwardRef<NaverAdRef, NaverAdProps>((props, ref) => {
const { adSlot, ...divProps } = props;
return <div {...divProps} ref={ref} id={adSlot.getAdSlotElementId()} />;
});
NaverAd.displayName = 'NaverAd';
Dispay ads
Display ads using the defined Hooks and components above.
import { useEffect } from 'react';
import { NaverAd } from './NaverAd';
import { useAdSlot } from './use-ad-slot';
import { useGladSdk } from './use-glad-sdk';
const adSlotInfo = {
adUnitId: 'ad_unit_id',
adSlotElementId: 'slot',
uct: 'KR',
customParam: {
category: 'entertainment',
hobby: ['music', 'sports'],
},
};
const MyComponent = () => {
const gladSdk = useGladSdk();
const adSlot = useAdSlot(adSlotInfo, {
onAdLoaded(ad: any) {
// @TODO implements code
},
onAdClicked(ad: any) {
// @TODO implements code
},
});
useEffect(() => {
gladSdk.displayAd(adSlot);
}, [gladSdk, adSlot]);
return (
<NaverAd adSlot={adSlot} />
);
}