Integration Guide
App Banner IntegrationDisplay contextual banners in your app in minutes using the ahdjs SDK. 1
Install the SDKAdd ahdjs to your project via NPM or a CDN script tag. NPM
# Install the package npm install ahdjs // Import in your entry file import AHDjs from 'ahdjs'; import 'ahdjs/build/css/index.css'; import 'https://pagepilot.fabbuilder.com/ahdStyles.css'; CDN
<!-- Add inside <head> --> <link rel="stylesheet" href="https://unpkg.com/ahdjs/build/css/index.css" /> <link rel="stylesheet" href="https://pagepilot.fabbuilder.com/ahdStyles.css" /> <script src="https://unpkg.com/ahdjs/build/index.js"></script> <!-- Access the constructor via window --> const AHDjs = window.AHDjs.default; 2
Initialize AHDjsCreate an instance with your Application ID and API host. const ahdJs = AHDjs(undefined, { applicationId: 'YOUR_APPLICATION_ID', // from the platform dashboard apiHost: 'https://pagepilot.fabbuilder.com', visitorId: 'CURRENT_USER_ID', // required for oneTimeOnly tracking showProgressbar: false, });
๐ก
Find your Application ID in the platform under Settings โ Integration.
โน๏ธ
visitorId is required for oneTimeOnly โ without it the SDK cannot track which visitors have already seen the banner.
3
Add a target elementPlace an empty <!-- The SDK injects rendered banner HTML into this element --> <div id="your-banner-identifier"></div>
โ ๏ธ
The
id must exactly match the identifier set when creating the banner in the platform.
4
Render the bannerCall // async/await await ahdJs.renderAppBanner('your-banner-identifier', true); // โ or โ promise chain ahdJs.renderAppBanner('your-banner-identifier', true) .then(() => console.log('Banner rendered')) .catch((err) => console.error(err));
โน๏ธ
Second parameter โ
truePassing true enables visitor-tracking. When oneTimeOnly is set on the banner record, the SDK uses this to suppress the banner for visitors who have already seen it. Always pass true when using oneTimeOnly.
5
Configure the banner in the platformCreate a banner record with these fields.
Behaviour options
โ
showCloseIcon
Renders a dismiss button in the corner of the banner. When clicked, the banner is hidden for that visitor.
๐
oneTimeOnly
Shows the banner only once per visitor. After they see or dismiss it, it will not appear again. To reset, call ahdJs.markUnacknowledge(identifier) โ see Step 7. Banner types
๐ข simpleBannerFlat inline strip๐ carouselSlideshow of slides๐ช modalOverlay dialog๐
Understanding the identifierThink of it as a name tag that tells the banner exactly where to appear. An identifier is simply a name you give your banner โ like a label on a parcel. You write the same name in three spots, and the system uses it to match the right banner to the right place on your page. If all three names match, the banner shows up. If even one is different, nothing appears.
๐ฆ
A simple way to picture it
Imagine labelling a parcel "Summer Sale", reserving a shelf also labelled "Summer Sale", and then asking a helper to "bring the Summer Sale parcel to the Summer Sale shelf." Same name in all three places = it ends up exactly where you want it. The same name goes in three places
1 ยท In the platform
โ
When you create the banner, you give it this name
2 ยท On your page
โ
You mark the empty spot where the banner should appear with the same name
3 ยท In the code
โ
You tell the system, by name, which banner to show
The same name everywhere โ here "summer-sale"
// 1 ยท In the platform, you name the banner: "summer-sale" <!-- 2 ยท On your page, you mark the spot with the same name --> <div id="summer-sale"></div> // 3 ยท In the code, you ask for it by the same name await ahdJs.renderAppBanner('summer-sale', true); A few simple rules
โ ๏ธ
Banner not showing up? The most common reason is a name that doesn't match. Check that the name is written exactly the same in all three places above โ a single typo or capital letter is enough to break it.
6
Enable One-Time-Only displayShow the banner exactly once per visitor โ then suppress it automatically. What oneTimeOnly does
How to enable it โ 2 steps
Step A โ Platform: Open your banner record in the dashboard and toggle oneTimeOnly to true. Step B โ Code: Pass a stable // 1. Initialize with a visitorId so the SDK can track this visitor const ahdJs = AHDjs(undefined, { applicationId: 'YOUR_APPLICATION_ID', apiHost: 'https://pagepilot.fabbuilder.com', visitorId: 'CURRENT_USER_ID', // โ required for oneTimeOnly showProgressbar: false, }); // 2. Render โ SDK silently skips if visitor has already seen it await ahdJs.renderAppBanner('your-banner-identifier', true); Runtime flow
renderAppBanner called
โ
Is oneTimeOnly enabled on this banner?
Yes
โ
Has this
visitorId already acknowledged it?
No โ first time
โ
Banner renders and acknowledgement is recorded
Yes โ already seen
โ
Banner suppressed โ container stays empty
No โ disabled
โ
Banner always renders on every call
Combining with showCloseIcon
When both oneTimeOnly and
โ
Best practice: enable both together for welcome banners, announcements, or promotional modals that should appear only on first visit.
โ ๏ธ
If
visitorId is omitted or changes between sessions, the SDK cannot link acknowledgements and may show the banner again. Always use a stable, unique identifier such as your internal user ID.
7
Re-show a one-time banner โ markUnacknowledge()Clear the acknowledgement so a oneTimeOnly banner is visible again.
Calling markUnacknowledge(identifier) clears the acknowledgement record for the current visitor, so the next // Clear acknowledgement for a specific banner await ahdJs.markUnacknowledge('your-banner-identifier'); // Re-render to show the banner again await ahdJs.renderAppBanner('your-banner-identifier', true);
โน๏ธ
Parameter
identifier โ the same unique string used in renderAppBanner and the banner record.
โ ๏ธ
This only affects the current visitor (identified by
visitorId). Other visitors are not affected.
Common use-cases
8
Responsive banners โ separate designs per screen sizeCreate a mobile-optimised and a desktop-optimised banner, then let CSS show only the right one.
Because the SDK renders into any Why two records instead of one adaptive banner?
Breakpoint reference
๐ฑ Mobile0 โ 767 px
๐ Tablet768 โ 1023 px
๐ฅ Desktopโฅ 1024 px
Adjust the CSS threshold to match your app's own layout breakpoints. Step A โ Create two banner records in the platform
Step B โ Add two container elements
<!-- Mobile container โ hidden on large screens via CSS --> <div id="welcome-banner-mobile" class="ahd-banner-mobile"></div> <!-- Desktop container โ hidden on small screens via CSS --> <div id="welcome-banner-desktop" class="ahd-banner-desktop"></div> Step C โ Hide the off-breakpoint container with CSS
/* Mobile-first: mobile banner visible by default, desktop hidden */ .ahd-banner-mobile { display: block; } .ahd-banner-desktop { display: none; } @media (min-width: 768px) { .ahd-banner-mobile { display: none; } .ahd-banner-desktop { display: block; } }
โ
Using CSS
display: none is the correct approach. The hidden container still exists in the DOM so the SDK can inject content, but the visitor never sees the wrong version.
โน๏ธ
Tailwind equivalents
class="block md:hidden" for the mobile container and
class="hidden md:block" for the desktop container.
Step D โ Render both banners with the SDK
const ahdJs = AHDjs(undefined, { applicationId: 'YOUR_APPLICATION_ID', apiHost: 'https://pagepilot.fabbuilder.com', visitorId: 'CURRENT_USER_ID', showProgressbar: false, }); // Render both in parallel โ CSS hides the one that doesn't apply await Promise.all([ ahdJs.renderAppBanner('welcome-banner-mobile', true), ahdJs.renderAppBanner('welcome-banner-desktop', true), ]); React / SPA usage
import { useEffect } from 'react'; import AHDjs from 'ahdjs'; import 'ahdjs/build/css/index.css'; export function WelcomeBanner({ userId }) { useEffect(() => { const ahdJs = AHDjs(undefined, { applicationId: 'YOUR_APPLICATION_ID', apiHost: 'https://pagepilot.fabbuilder.com', visitorId: userId, showProgressbar: false, }); Promise.all([ ahdJs.renderAppBanner('welcome-banner-mobile', true), ahdJs.renderAppBanner('welcome-banner-desktop', true), ]); }, [userId]); return ( <> <div id="welcome-banner-mobile" className="ahd-banner-mobile" /> <div id="welcome-banner-desktop" className="ahd-banner-desktop" /> </> ); } Platform design checklist per record
๐ฑ Mobile record
identifier: welcome-banner-mobile โข Narrow image / icon (โค 360 px) โข Short, single-line headline โข Full-width CTA button โข Larger font size (โฅ 16 px) โข Vertical / stacked layout โข oneTimeOnly: true โข showCloseIcon: true ๐ฅ Desktop record
identifier: welcome-banner-desktop โข Wide image / illustration โข Longer headline + subtitle โข Inline CTA button (right-aligned) โข Standard font size (14โ15 px) โข Horizontal / side-by-side layout โข oneTimeOnly: true โข showCloseIcon: true 9
Multi-language banners โ localized content per visitorPass a How language filtering works
Step A โ Tag your banner content in the platform
Open the banner record in the dashboard. For each language you want to support, create a content variant and set its Language field to the matching code. Leave one variant's field empty or set it to your default language โ this acts as the fallback.
Step B โ Pass the language at initialization
const ahdJs = AHDjs(undefined, { applicationId: 'YOUR_APPLICATION_ID', apiHost: 'https://pagepilot.fabbuilder.com', visitorId: 'CURRENT_USER_ID', showProgressbar: false, language: 'es', // โ ISO code; omit or pass '' to use the platform default }); await ahdJs.renderAppBanner('your-banner-identifier', true);
โน๏ธ
Omitting
language (or passing '') tells the API to return the platform's default variant โ typically the English content.
Step C โ Switch language at runtime with
setLanguage()
Call // Called when the user picks a new language in your UI await ahdJs.setLanguage('pt-BR'); // Then re-render to show the localized banner await ahdJs.renderAppBanner('your-banner-identifier', true);
โ ๏ธ
setLanguage() clears the entire SDK cache โ banners, tours, highlights, and visitor stats. Call it only when the user actively switches their language preference, not on every render.
Runtime flow
renderAppBanner called
โ
API request includes
filter[language]=<code>
Matching variant found
โ
Localized banner content is rendered
No matching variant
โ
Platform falls back to the default-language variant
language: '' (empty)
โ
Platform always serves the default variant
React / SPA โ subscribe to app language changes
Create the SDK instance once and call import { useEffect, useRef } from 'react'; import AHDjs from 'ahdjs'; import 'ahdjs/build/css/index.css'; export function LocalizedBanner({ userId, language }) { const ahdRef = useRef(null); // Create the instance once per user useEffect(() => { ahdRef.current = AHDjs(undefined, { applicationId: 'YOUR_APPLICATION_ID', apiHost: 'https://pagepilot.fabbuilder.com', visitorId: userId, showProgressbar: false, language: language || 'en', }); ahdRef.current.renderAppBanner('your-banner-identifier', true); }, [userId]); // Re-render only when language changes โ no instance recreation useEffect(() => { if (!ahdRef.current || !language) return; ahdRef.current.setLanguage(language).then(() => ahdRef.current.renderAppBanner('your-banner-identifier', true) ); }, [language]); return <div id="your-banner-identifier" />; }
โ ๏ธ
Keep
userId and language in separate useEffect hooks. Combining them recreates the SDK instance on every language change, which resets the oneTimeOnly check and causes unnecessary network calls.
oneTimeOnly with multi-language banners
Acknowledgements are stored against the
โ
Acknowledgement covers all language variants
A visitor who saw the English banner will not see it again when they switch to Spanish โ the same identifier is acknowledged regardless of language.
โน๏ธ
Re-show after adding a new translation
After publishing a new language variant, call markUnacknowledge('your-banner-identifier') to reset acknowledgements so all visitors can see the updated content.
โ ๏ธ
The
language option is a content filter, not a separate banner record. All language variants share the same identifier, oneTimeOnly flag, and acknowledgement record.
โ
Complete exampleDrop-in snippet โ replace the highlighted values. <link rel="stylesheet" href="https://unpkg.com/ahdjs/build/css/index.css" /> <script src="https://unpkg.com/ahdjs/build/index.js"></script> <div id="welcome-banner"></div> <button onclick="resetBanner()">Show banner again</button> <script> const AHDjs = window.AHDjs.default; const ahdJs = AHDjs(undefined, { applicationId: 'YOUR_APPLICATION_ID', apiHost: 'https://pagepilot.fabbuilder.com', visitorId: 'user-123', // โ stable unique user ID showProgressbar: false, language: 'en', // โ optional: ISO code for localized content }); // Renders on load; skipped silently if visitor already saw it and oneTimeOnly=true ahdJs.renderAppBanner('welcome-banner', true); async function resetBanner() { await ahdJs.markUnacknowledge('welcome-banner'); await ahdJs.renderAppBanner('welcome-banner', true); } // Switch language at runtime async function switchLanguage(code) { await ahdJs.setLanguage(code); await ahdJs.renderAppBanner('welcome-banner', true); } </script> Complete responsive + multi-language example
<link rel="stylesheet" href="https://unpkg.com/ahdjs/build/css/index.css" /> <script src="https://unpkg.com/ahdjs/build/index.js"></script> <div id="welcome-banner-mobile" class="ahd-banner-mobile"></div> <div id="welcome-banner-desktop" class="ahd-banner-desktop"></div> <style> .ahd-banner-mobile { display: block; } .ahd-banner-desktop { display: none; } @media (min-width: 768px) { .ahd-banner-mobile { display: none; } .ahd-banner-desktop { display: block; } } </style> <script> const AHDjs = window.AHDjs.default; const ahdJs = AHDjs(undefined, { applicationId: 'YOUR_APPLICATION_ID', // โ replace apiHost: 'https://pagepilot.fabbuilder.com', visitorId: 'user-123', // โ replace with real user ID showProgressbar: false, language: 'en', // โ replace with visitor's locale }); Promise.all([ ahdJs.renderAppBanner('welcome-banner-mobile', true), ahdJs.renderAppBanner('welcome-banner-desktop', true), ]); // Call this whenever the visitor changes their language preference async function switchLanguage(code) { await ahdJs.setLanguage(code); await Promise.all([ ahdJs.renderAppBanner('welcome-banner-mobile', true), ahdJs.renderAppBanner('welcome-banner-desktop', true), ]); } </script> |