@financial-times/ads-display
This package provides functionality to display ads and track user behaviour for ft.com.
Table of Contents
1. Install
2. Ads Data
3. Display Ads
4. Permutive
5. Client Side API
6. Server Side API
7. Monitoring
8. Third Party Scripts
9. Develop
10. Releasing
Install
This package is available on npm.
npm install --save @financial-times/ads-display
Ads Data
If you need to get some metadata about the page or user, you can use getAdsData()
.
How to use
import { getAdsData } from '@financial-times/ads-display'
getAdsData({
user: true,
page: {
type: 'article'
id: 'id-of-the-page'
}
}).then(adsData => {
})
Developer note: You can also override the host and version of the ads-api endpoints which this function fetches from by providing a host
property. This is useful if you want to test locally.
getAdsData({
...,
host: 'http://local.ft.com:3002/v2'
})
The default host is https://ads-api.ft.com/v2
See Client-side API section for more details
Display Ads
Both client and server side components for displaying ads on a page.
Server side
You can define ad slots and pass data to the client for initialising the ads library
import ReactDOM from 'react-dom/server'
import { Slot, AdsOptionsEmbed } from '@financial-times/ads-display'
app.use(appContextMiddleware)
app.get('/my-page-with-ads', (req, res, next) => {
const adOptions = {
appName: res.locals.appContext.appName,
adUnit: ['ft.com', `articles/${req.articleId}`],
targeting: {
user: {},
article: {},
stream: {}
},
...
}
const slotProps = {
name: 'leaderboard',
formatSmall: false,
formatsLarge: 'SuperLeaderboard,Leaderboard,Responsive',
formatsExtra: 'Billboard,SuperLeaderboard,Leaderboard,Responsive',
targeting: {
specialAdTargeting: true,
pos: 'top'
}
}
const adHTML = ReactDOM.renderToStaticMarkup(
<AdsOptionsEmbed options={adOptions} />
<Slot {...slotProps} />
)
res.render(adHTML)
})
Client side
If you haven't defined your ad slots on the server, you will first need to define one or more ad slots on the page. You can learn more about this here
Once you have your ad slots defined in your HTML, you will need to initialise the ads library.
Simple example
This is the minimum you need to get some ads.
import { displayAds } from '@financial-times/ads-display'
import * as flags from '@financial-times/anvil-ui-ft-flags'
const flagsClient = flags.init()
const options = {
appName: 'my-app'
}
displayAds.init(options, flagsClient);
Pass in the rootid
On ft.com, we will want to pass in a page rootID to each ad call. ads-display exports some utility functions, amongst which a method getRootId()
which can be called when initialising the ads.
import { displayAds, adsUtils } from '@financial-times/ads-display'
import * as flags from '@financial-times/anvil-ui-ft-flags'
const flagsClient = flags.init()
const { appName, abTestState } = appContext
const options = {
appName,
abTestState,
rootId: adsUtils.getRootID()
}
if(flags.get('ads')) {
displayAds.init(options, flagsClient);
}
Get ads data first
In most use cases on ft.com and the FT app, we will want to get some data about the user and the page and append this to our ad requests to provide more targeted ads.
import { getAdsData, displayAds } from '@financial-times/ads-display'
import * as flags from '@financial-times/anvil-ui-ft-flags'
const flagsClient = flags.init()
const { appName, contentId, abTestState } = appContext
if(flags.get('ads')) {
getAdsData({
user: true,
page: {
type: 'article',
id: contentId
}
}).then(adsData => {
const options = {
appName,
abTestState
targeting: adsData.metadata,
adUnit: adsData.adUnit,
};
displayAds.init(options, flagsClient);
});
}
Get ads data & enable permutive
Here is an example of using ads-display on the ft.com article page. This ties in all parts of ads-display, by first fetching some data, then enabling both the display ads and the behaviour tracking with permutive
import {
getAdsData,
adsUtils,
displayAds,
adsPermutive
} from '@financial-times/ads-display'
import * as flags from '@financial-times/anvil-ui-ft-flags'
const flagsClient = flags.init()
if(flags.get('ads')) {
getAdsData({
user: true,
page: {
type: 'article',
id: appContext.get('contentId')
}
}).then(adsData => {
displayAds.init({
appName: appContext.get('appName'),
abTestState: appContext.get('abTestState'),
rootId: getRootId(),
targeting: adsData.metadata,
adUnit: adsData.adUnit,
}, flagsClient);
if (flags.get('AdsPermutive')) {
adsPermutive.setup();
adsPermutive.loadPermutiveScript();
adsPermutive.identifyUser({
uuid: adsData.metadata.user.uuid,
spoorId: adsData.metadata.user.spoorId
})
const pageView = adsPermutive.fromAdsDataToPageView({
...adsData.metadata,
rootId: getRootID(),
type: appContext.get('appName')
});
adsPermutive.trackPageView(pageView);
}
});
}
Options
These are all the options you can pass to displayAds.init()
server side via AdsOptionsEmbed
appName
(required) <String>
- Name of the appadUnit
: (optional) <Array>
- An array to use for the GPT ad unit. Each item in the array will be concatenated with a /
. E.g. options.adUnit = ['ft.com', 'UK']
becomes ft.com/UK
. If not provided, will default to unclassified
.adsLayout
: Used for setting the nLayout
value in the ad call, useful for ad ops for targeting purposes, to test different types of ads formats and for setting up demosabTestState
(optional) <String>
- The state of all the mvt tests running on the page. Usually found in appContext
for pageKit apps.rootId
(optional) <String>
- The root id of an ft.com page. You can get this using the getRootId()
function.targeting
(optional) <Object>
- An object containing the metadata from the ads data manager to be used as targeting for the ad calls.sandbox
(optional) <Boolean>
- Determines wether to load test ads from the origami ad unit in Google Ad ManagerdisableMonitoring
(optional) <Boolean>
- When true
, ads-display
will not send any ads lifecycle metrics to Spoor API.formats
(optional) <Object>
- Object containing custom ad slot formats. For example:
{
MyAdSlot: { sizes: [[970,400], [970,250]] }
}
lazyLoadMargins
(optional) <Object>
- Object containing lazy loading margins to apply below a certain viewport side. For example:
{
1000: '10%',
400: '5%'
}
waitForMoat
: (optional) <Boolean>
- Will wait 1 second max for the moat prebid api to be loaded before initialising any ad slot so that we can get invalid traffic data with the ad requests
Permutive {#permutive}
Permutive is a behaviour tracking tool that is used to segment people based on their activity on the site. See adsPermutive for more details. It is proxied here by ads-display for convenience. Here's an example:
Basic example
import { adsPermutive } from '@financial-times/ads-display';
adsPermutive.setup();
adsPermutive.loadPermutiveScript();
adsPermutive.identifyUser({
guid: '1234',
spoorId: '12345'
});
adsPermutive.trackPageView({
page: {
type: 'article',
rootId: '1234'
}
});
FT.com helper function
For ft.com, there is a helper function that calls all the functions, with some data about the page and user (usually fetched from getAdsData()
), type and rootId.
import { adsUtils } from '@financial-times/ads-display';
adsUtils.enablePermutiveFtCom({
metadata: { user: {...}, page: {...} },
type: 'article',
rootId: adsUtils.getRootID()
})
Client-side API
displayAds
import { displayAds } from '@financial-times/ads-display
displayAds.init(options: nAdsOptions, flags: FlagsClient)
This function initalises o-ads, loads the required third party scripts, and scans the page for ad slots to initialise
displayAds.validateTraffic(deferScript: boolean, callback: function)
Check weather the request is deemed as valid traffic (using an external script from moat). Talks directly to the global window.googletag
object to set an extra targeting parameter m_data
= 0
(valid) or 1
(invalid) for every ad call. Filtering of ads is done on the Google Ad Manager side based on these targeting parameters. There is a race condition between displayAds.validateTraffic()
and displayAds.init()
so the first ad call may not have the right value for the m_data
parameter.
deferScript
: Defer loading of the moat script, true by defaultcallback
: Called when the moat script is loaded
adsUtils
import { adsUtils } from '@financial-times/ads-display
adsUtils.getRootId()
Returns the root id of the current ft.com page
adsUtils.enablePermutiveFtCom()
A helper function to run all the necessary functions for loading and sending data to permutive on ft.com pages.
getAdsData(options: { user, page: { type, id }, host })
{#data-manager}
import { getAdsData } from '@financial-times/ads-display
or to minimize bunle size (depending on your build config), you can import directly:
ES Module: import { getAdsData } from '@financial-times/ads-display/dist/esm/adsData
CommonJS: import { getAdsData } from '@financial-times/ads-display/dist/cjs/adsData
Returns a data object in the following format:
{
metadata: {
user: {
},
article: {
}
stream: {
}
},
adUnit: []
}
formatUserData(userData)
Format the user data as key => value pairs for use in ad requests. The keys are mapped from the ads-api /v2/user response to the equivalent keys that the ad ops server expects in the ad calls.
Import
import { formatUserData } from '@financial-times/ads-display
or to minimize bunle size (depending on your build config), you can import directly:
ES Module: import { formatUserData } from '@financial-times/ads-display/dist/esm/adsData
CommonJS: import { formatUserData } from '@financial-times/ads-display/dist/cjs/adsData
Usage
getAdsData({ user: true }).then(adsData => {
const keyValuePairs = formatUserData(adsData.metadata.user);
...
})
formatArticleData(articleData)
Format the article data as key => value pairs for use in ad requests. The keys are mapped from the ads-api /v2/user response to the equivalent keys that the ad ops server expects in the ad calls.
Import
import { formatArticleData } from '@financial-times/ads-display
or to minimize bunle size (depending on your build config), you can import directly:
ES Module: import { formatArticleData } from '@financial-times/ads-display/dist/esm/adsData
CommonJS: import { formatArticleData } from '@financial-times/ads-display/dist/cjs/adsData
Usage
getAdsData({
page: {
type: 'article',
id: appContext.get('contentId')
}
}).then(adsData => {
const keyValuePairs = formatArticleData(adsData.metadata.article);
...
})
adsPermutive
import { adsPermutive } from '@financial-times/ads-display
Exposes all the public functions from adsPermutive
Server-side API
AdsOptionsEmbed
AdsOptionsEmbed
is a JSX component that renders a script with the passed options embedded in a json blob, they are then retrieved automatically client side by ads-display
Props
Slot
Slot
is a JSX component that renders an Ad slot compatible with o-ads
Props
name
<String>
: Ad slot nameformats
<Array|String>
: Available formats to usetargeting
<Object>
: Slot level targeting object ex: { pos: 'top', mvtGroup: 'specialChristmasAd'...}
formatsDefault
<Array|String>
List of formats to use as a defaultformatsSmall
<Array|String>
List of formats to use for small sized screensformatsMedium
<Array|String>
List of formats to use for medium sized screensformatsLarge
<Array|String>
List of formats to use for large sized screensformatsExtra
<Array|String>
List of formats to use for extra large sized screensclassNames
<Array|String>
CSS class namesstyle
<String>
: Custom CSS styles
presets
:warning: not yet available. Will contain preset Slots
props for the most common ads on FT sites such as Leaderboard, mid, right handrail
Monitoring
The following o-ads
events will be tracked:
page-initialised
slot-requested
slot-rendered
slot-collapsed
This tracking can be disabled through the disableMonitoring
option.
Each of those o-ads
events will be sent as an oTracking event to Spoor API and will contain several time marks as specified in the metrics config object alongside additional properties to identify the associated slot. The events will be sent to Spoor for all users.
Third party scripts
Ads tracking relies on third party scripts to add extra tracking, reporting, counter bot-traffic measure and more.
GPT
It's the client site google library that o-ads is built on top of to load ads on the site
Where?
GPT is inserted by o-ads
, which is a dependency of this package
Why?
It is necessary to load ads
Moat
Moat is an analytics tool that has an invalid traffic functionality, brand safety and extra tracking that google doesn't get
Where?
Moat is inserted on the page when nAds.init()
is called
Why?
As part of our ad offering, these functionalities are required
Permutive
Permutive is the behaviour tracking software that segments users based on their actions on site.
Where?
permutive is inserted by @financial-times/adsPermutive
if the adsPermutive
flag is on
Why?
As part of our ad offering, these functionalities are required
Demos
If you are developing on ads-display, you may find it useful to run the demo page. This mocks a consuming app where you would normally import ads-display from.
make demo
- run a demo server on http
make demo-https
- run the demos on https. This is useful if you want to test under https://local.ft.com
to make sure there are no cross origin request errors.
There are two demos available:
- promise-demo.html
- async-await-demo.html
These are examples of how to implement ads-display using async/await method or promises.
Releasing
In order to release a new version please refer to the page-kit release guidelines