Google Publisher Tag Ads in Single Page Application (Next.js) [CASE STUDY]

How the ads work?

<!--
First two scripts are simple:
load GPT library and initialize googletag and the command queue.
They can be placed in the site's <head>
-->
<script
async="async"
src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"
></script>
<script>
var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];
</script>
<!--
This script creates "ad unit" that will be then displayed on the page
-->
<script>
googletag.cmd.push(function () {
/**
Mapping assigns sizes of an ad unit to corresponding breakpoints,
it makes the ad responsive
*/
var mapping = googletag
.sizeMapping()
.addSize([1100, 0], [[750, 200]])
.addSize([960, 0], [[468, 60]])
.addSize([0, 0], [])
.build();
/**
Now the slot is being defined.
The function accepts three arguments:
- id of the slot (set by the agency in their ad management console),
- array of sizes (also set by them),
- id of the div in which the ad should be displayed
*/
googletag
.defineSlot(
"/52555387/XYZ.pl_750x200_7_a_d",
[
[750, 200],
[468, 60],
],
"div-gpt-ad-XYZ.pl_750x200_7_a_d"
)
.defineSizeMapping(mapping)
.addService(googletag.pubads());
googletag.enableServices();
});
</script>
<!--
The part below should be placed in the body of the page
It is a div with id the same as in "defineSlot()" function
and a script that displays a defined slot inside that div
-->
<div id="div-gpt-ad-XYZ.pl_750x200_7_a_d">
<script>
googletag.cmd.push(function () {
googletag.display("div-gpt-ad-XYZ.pl_750x200_7_a_d");
});
</script>
</div>
</script></div>
  1. GPT script is loaded.
  2. Ad slot with a specific ID is defined with its size and a target div’s ID. It then stays in the memory.
  3. googletag.display()function is executed that makes a request for the specific ad slot.
  4. HTML fetched in the request above is being placed inside the div. It is usually an<iframe>that displays the ad.
  • Defined ad slot stays in the memory until page reload.
  • An ad with a specific ID can only be displayed once on the page — trying to rungoogletag.display()on an already displayed ID will not work.

The problem

  1. googletag.display() is not executed so new ads are not fetched.

Solution

  • when the ad component is rendered, it defines the ad slot and displays it,
  • when route is changed, all defined slots are removed from the memory,
  • after going to a different page, ad components on that page define new slots and display them.
const.js
const ads = {
"750x200_7_a_d": {
sizes: [[750, 200],[468, 60]],
mapping: {
0: [],
960: [468, 60],
1100: [750, 200],
}
},
...
}
useAdSlot.js
import { useEffect } from "react";
export function useAdSlot({ mapping, sizes, id, isTransitioning }) {
useEffect(() => {
if (!isTransitioning && typeof window !== undefined) {
const { googletag } = window;
googletag.cmd.push(function () {
const adMapping = googletag.sizeMapping();
Object.keys(mapping).forEach((breakpoint) => {
adMapping.addSize([Number(breakpoint), 0], [mapping[breakpoint]]);
});
const builtMapping = adMapping.build();
googletag
.defineSlot(
`/52555387/XYZ.pl_${id}`,
sizes,
`div-gpt-ad-XYZ.pl_${id}`
)
.defineSizeMapping(builtMapping)
.addService(googletag.pubads());
googletag.enableServices();
});
googletag.cmd.push(function () {
googletag.display(`div-gpt-ad-XYZ.pl_${id}`);
});
}
}, [mapping, sizes, id, isTransitioning]);
}
Ad.js
import React from "react";
import { useTransitionState } from "@/containers/TransitionState";
import { useAdSlot } from "@/hooks/useAds";
import { ads } from "./const";
function Ad({ adId }) {
const { isTransitioning } = useTransitionState();
const ad = ads[adId];
useAdSlot({
mapping: ad.mapping,
sizes: ad.sizes,
id: adId,
isTransitioning,
});
return <div id={`div-gpt-ad-XYZ.pl_${adId}`} />;
}
export default Ad;
setTransitionStarted = () => {
this.setState({ isTransitioning: true });
// destroy all ad slots
const { googletag } = window;
googletag.cmd.push(function () {
googletag.destroySlots();
});
};
setTransitionComplete = () => {
this.setState({ isTransitioning: false });
};
componentDidMount() {
Router.events.on("routeChangeStart", this.setTransitionStarted);
Router.events.on("routeChangeComplete", this.setTransitionComplete);
}
componentWillUnmount() {
Router.events.off("routeChangeStart", this.setTransitionStarted);
Router.events.off("routeChangeComplete", this.setTransitionComplete);
}

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Monterail

Monterail

508 Followers

A close-knit team of 170+ experts offering Web & mobile development for startups and businesses.