{"id":669,"date":"2026-04-03T13:00:00","date_gmt":"2026-04-03T18:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=669"},"modified":"2026-03-07T03:33:14","modified_gmt":"2026-03-07T08:33:14","slug":"banner-sdk-integration","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/banner-sdk-integration\/","title":{"rendered":"Integrating Smart Banners with the Tolinku Web SDK"},"content":{"rendered":"\n<p>This tutorial walks through adding Tolinku smart banners to a web project from scratch. It is written for developers doing the actual integration, so it covers the specifics: the exact configuration options, how the customization API works, what events you can listen to, and how to verify things are working correctly before going to production.<\/p>\n\n\n\n<p>If you are evaluating whether Tolinku banners fit your use case, the <a href=\"https:\/\/tolinku.com\/features\/smart-banners\">smart banners feature page<\/a> has the higher-level overview. This tutorial assumes you have already made that decision and are ready to ship.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A Tolinku account with an Appspace created for your app<\/li>\n<li>Your Tolinku publishable API key (<code>tolk_pub_...<\/code>)<\/li>\n<li>An iOS or Android app published on the App Store or Google Play<\/li>\n<li>A web project where you can add a script tag or npm package<\/li>\n<\/ul>\n\n\n\n<p>If you have not set up your Appspace yet, the <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/smart-banners\/\">Tolinku smart banners documentation<\/a> covers the dashboard configuration steps that precede the code integration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installation<\/h2>\n\n\n\n<p>There are two ways to load the Tolinku Web SDK: via a script tag, or via npm. Use npm if you are working in a bundled JavaScript application (React, Vue, SvelteKit, Next.js, etc.). Use the script tag for WordPress sites, plain HTML, or any situation where you do not have a JavaScript build step.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Option 1: Script Tag<\/h3>\n\n\n\n<p>Add the following to the <code>&lt;head&gt;<\/code> of your HTML, before the closing <code>&lt;\/head&gt;<\/code> tag:<\/p>\n\n\n\n<pre><code class=\"language-html\">&lt;script\n  async\n  src=&quot;https:\/\/cdn.tolinku.com\/banner.js&quot;\n  data-tolk-key=&quot;tolk_pub_your_key_here&quot;\n  data-tolk-app-id=&quot;your_appspace_id&quot;\n&gt;&lt;\/script&gt;\n<\/code><\/pre>\n\n\n\n<p>Using <code>async<\/code> ensures the script does not block HTML parsing. The banner will initialize after the DOM is ready.<\/p>\n\n\n\n<p>For sites where you want to minimize impact on initial page load, <code>defer<\/code> is an alternative:<\/p>\n\n\n\n<pre><code class=\"language-html\">&lt;script\n  defer\n  src=&quot;https:\/\/cdn.tolinku.com\/banner.js&quot;\n  data-tolk-key=&quot;tolk_pub_your_key_here&quot;\n  data-tolk-app-id=&quot;your_appspace_id&quot;\n&gt;&lt;\/script&gt;\n<\/code><\/pre>\n\n\n\n<p><code>defer<\/code> guarantees execution order (scripts with <code>defer<\/code> run in order after HTML parsing completes), while <code>async<\/code> does not. Use <code>defer<\/code> if you have other scripts that depend on the banner SDK being available.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Option 2: npm<\/h3>\n\n\n\n<pre><code class=\"language-bash\">npm install @tolinku\/web-sdk\n<\/code><\/pre>\n\n\n\n<p>Then in your application entry point:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">import { Tolinku } from &#39;@tolinku\/web-sdk&#39;;\n\nconst tolinku = new Tolinku({\n  key: &#39;tolk_pub_your_key_here&#39;,\n  appId: &#39;your_appspace_id&#39;,\n});\n\ntolinku.init();\n<\/code><\/pre>\n\n\n\n<p>For TypeScript projects, the SDK ships with type definitions:<\/p>\n\n\n\n<pre><code class=\"language-typescript\">import { Tolinku, TolinkuConfig } from &#39;@tolinku\/web-sdk&#39;;\n\nconst config: TolinkuConfig = {\n  key: &#39;tolk_pub_your_key_here&#39;,\n  appId: &#39;your_appspace_id&#39;,\n};\n\nconst tolinku = new Tolinku(config);\ntolinku.init();\n<\/code><\/pre>\n\n\n\n<p>The <a href=\"https:\/\/tolinku.com\/docs\/developer\/sdks\/web\/\">full Web SDK documentation<\/a> covers the complete TypeScript interface.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Basic Configuration<\/h2>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/platform-platform-banners.png\" alt=\"Tolinku smart banner configuration in the dashboard\"><\/p>\n\n\n\n<p>After initialization, the SDK reads your Appspace&#39;s banner configuration from the Tolinku API and renders the banner according to the rules you have set in the dashboard. The minimum configuration (key + appId) is sufficient to get a working banner with your dashboard settings.<\/p>\n\n\n\n<p>For more control at the code level, the <code>Tolinku<\/code> constructor accepts a full configuration object:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">const tolinku = new Tolinku({\n  key: &#39;tolk_pub_your_key_here&#39;,\n  appId: &#39;your_appspace_id&#39;,\n\n  \/\/ Banner display options\n  banner: {\n    position: &#39;top&#39;,         \/\/ &#39;top&#39; or &#39;bottom&#39;\n    showDelay: 0,            \/\/ milliseconds before showing (0 = immediate)\n    scrollTrigger: 0,        \/\/ show after N pixels scrolled (0 = disabled)\n    sessionCap: 1,           \/\/ max impressions per session\n    dismissalTtl: 604800,    \/\/ seconds before showing again after dismiss (7 days)\n  },\n\n  \/\/ Deep link configuration\n  deepLink: {\n    useCurrentUrl: true,     \/\/ pass current page URL as deep link context\n    customParams: {},        \/\/ additional params to pass through\n  },\n});\n<\/code><\/pre>\n\n\n\n<p>All of these options have defaults that match the recommended settings for most implementations. You typically only need to override what differs from the defaults.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Position<\/h3>\n\n\n\n<p><code>&#39;top&#39;<\/code> renders the banner at the top of the viewport as a fixed bar. <code>&#39;bottom&#39;<\/code> renders it at the bottom. The top position is more visible; the bottom position is less disruptive to reading. Default is <code>&#39;top&#39;<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">showDelay<\/h3>\n\n\n\n<p>Milliseconds to wait after page load before rendering the banner. A value of 1000-2000ms means the user can confirm they have landed on useful content before seeing the prompt. For pages with significant organic search traffic, a small delay is a reasonable courtesy. Default is <code>0<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">scrollTrigger<\/h3>\n\n\n\n<p>If set to a positive number, the banner only appears after the user has scrolled at least that many pixels. <code>scrollTrigger: 300<\/code> means the banner appears after a typical mobile scroll. This is mutually exclusive with <code>showDelay<\/code> in most use cases; pick one. Default is <code>0<\/code> (disabled).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">sessionCap<\/h3>\n\n\n\n<p>Maximum number of times the banner is shown within a single browser session. Setting this to <code>1<\/code> means users see the banner at most once per session, even if they navigate to multiple pages. Default is <code>1<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">dismissalTtl<\/h3>\n\n\n\n<p>After a user dismisses the banner, how many seconds before it shows again. The default is 604800 (7 days). Set to <code>0<\/code> to suppress the banner permanently once dismissed (stored in <code>localStorage<\/code>). Set to a lower value for high-traffic sites where you expect a lot of new visitors.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Customization API<\/h2>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/platform-platform-api-keys.png\" alt=\"Tolinku API key management for SDK integration\"><\/p>\n\n\n\n<p>The configuration object covers structural options, but visual customization is handled through CSS custom properties (CSS variables). The Tolinku banner injects a <code>&lt;div class=&quot;tolk-banner&quot;&gt;<\/code> element into the DOM and uses a defined set of CSS variables for theming.<\/p>\n\n\n\n<p>You can override any of these in your stylesheet:<\/p>\n\n\n\n<pre><code class=\"language-css\">:root {\n  --tolk-banner-bg: #1a1a2e;\n  --tolk-banner-text: #ffffff;\n  --tolk-banner-cta-bg: #4f46e5;\n  --tolk-banner-cta-text: #ffffff;\n  --tolk-banner-height: 80px;\n  --tolk-banner-font-family: inherit;\n  --tolk-banner-border-radius: 0px;\n  --tolk-banner-app-icon-size: 48px;\n}\n<\/code><\/pre>\n\n\n\n<p>The <code>inherit<\/code> value on <code>font-family<\/code> means the banner picks up your site&#39;s typeface automatically, which is usually what you want for a cohesive look.<\/p>\n\n\n\n<p>For the app icon, the SDK fetches it from the App Store or Google Play automatically using the app identifiers in your Appspace configuration. You do not need to host the icon yourself.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Custom message text<\/h3>\n\n\n\n<p>To override the banner message at the code level (rather than in the dashboard), use the <code>message<\/code> option:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">const tolinku = new Tolinku({\n  key: &#39;tolk_pub_your_key_here&#39;,\n  appId: &#39;your_appspace_id&#39;,\n  banner: {\n    message: &#39;Get the full experience in the app&#39;,\n    ctaText: &#39;Open App&#39;,\n  },\n});\n<\/code><\/pre>\n\n\n\n<p>For dynamic messages based on page content, you can read from the DOM before initializing:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">const productName = document.querySelector(&#39;[data-product-name]&#39;)?.textContent;\n\nconst tolinku = new Tolinku({\n  key: &#39;tolk_pub_your_key_here&#39;,\n  appId: &#39;your_appspace_id&#39;,\n  banner: {\n    message: productName\n      ? `See ${productName} in the app`\n      : &#39;Get the full experience in the app&#39;,\n    ctaText: &#39;Open App&#39;,\n  },\n});\n<\/code><\/pre>\n\n\n\n<p>The <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/smart-banners\/designing\/\">designing smart banners guide<\/a> covers all the visual customization options available from the dashboard, which are more extensive than the CSS variable approach.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Event Callbacks<\/h2>\n\n\n\n<p>The SDK emits events at key points in the banner lifecycle. You can listen to these to integrate banner behavior with your analytics, A\/B testing, or other systems.<\/p>\n\n\n\n<pre><code class=\"language-javascript\">const tolinku = new Tolinku({\n  key: &#39;tolk_pub_your_key_here&#39;,\n  appId: &#39;your_appspace_id&#39;,\n});\n\n\/\/ Banner became visible\ntolinku.on(&#39;banner:shown&#39;, (data) =&gt; {\n  console.log(&#39;Banner shown&#39;, data);\n  \/\/ Send to your analytics\n  analytics.track(&#39;App Banner Shown&#39;, {\n    page: window.location.pathname,\n    variant: data.variant,\n  });\n});\n\n\/\/ User tapped the CTA button\ntolinku.on(&#39;banner:clicked&#39;, (data) =&gt; {\n  console.log(&#39;Banner clicked&#39;, data);\n  analytics.track(&#39;App Banner Clicked&#39;, {\n    page: window.location.pathname,\n    destination: data.destination, \/\/ &#39;app_store&#39; | &#39;app_open&#39; | &#39;play_store&#39;\n  });\n});\n\n\/\/ User tapped the dismiss button\ntolinku.on(&#39;banner:dismissed&#39;, () =&gt; {\n  analytics.track(&#39;App Banner Dismissed&#39;, {\n    page: window.location.pathname,\n  });\n});\n\n\/\/ Banner was not shown (and why)\ntolinku.on(&#39;banner:suppressed&#39;, (reason) =&gt; {\n  console.log(&#39;Banner suppressed:&#39;, reason);\n  \/\/ reason values: &#39;session_cap&#39;, &#39;dismissed_recently&#39;, &#39;app_installed&#39;, &#39;platform_unsupported&#39;\n});\n\ntolinku.init();\n<\/code><\/pre>\n\n\n\n<p>Note that <code>tolinku.on()<\/code> calls must be made before <code>tolinku.init()<\/code>. Events emitted before listeners are registered are not replayed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Available events<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Event<\/th>\n<th>Description<\/th>\n<th>Data<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td><code>banner:shown<\/code><\/td>\n<td>Banner is rendered and visible<\/td>\n<td><code>{ variant, platform }<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>banner:clicked<\/code><\/td>\n<td>CTA button tapped<\/td>\n<td><code>{ destination, deepLinkUrl }<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>banner:dismissed<\/code><\/td>\n<td>Dismiss button tapped<\/td>\n<td>none<\/td>\n<\/tr>\n<tr>\n<td><code>banner:suppressed<\/code><\/td>\n<td>Banner would have shown but was suppressed<\/td>\n<td><code>reason: string<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>deeplink:resolved<\/code><\/td>\n<td>Deep link URL was generated<\/td>\n<td><code>{ url }<\/code><\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Programmatic Control<\/h2>\n\n\n\n<p>For single-page applications where route changes do not trigger a full page reload, you may want to control when the banner is shown or update its content on navigation.<\/p>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ Manually trigger banner show\ntolinku.showBanner();\n\n\/\/ Manually hide without dismissal (does not set dismissal cookie)\ntolinku.hideBanner();\n\n\/\/ Update banner message for the current page\ntolinku.updateBanner({\n  message: &#39;New message for this page&#39;,\n  deepLinkUrl: &#39;https:\/\/yourdomain.com\/current-page&#39;,\n});\n\n\/\/ Check current state\nconst state = tolinku.getBannerState();\n\/\/ { visible: boolean, dismissed: boolean, suppressed: boolean }\n<\/code><\/pre>\n\n\n\n<p>For React applications, a common pattern is to call <code>updateBanner()<\/code> inside a <code>useEffect<\/code> on route change:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">import { useEffect } from &#39;react&#39;;\nimport { useLocation } from &#39;react-router-dom&#39;;\n\nfunction AppBannerUpdater({ tolinku }) {\n  const location = useLocation();\n\n  useEffect(() =&gt; {\n    tolinku.updateBanner({\n      deepLinkUrl: window.location.href,\n    });\n  }, [location.pathname]);\n\n  return null;\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Testing Your Integration<\/h2>\n\n\n\n<p>Before deploying to production, verify the integration with these steps.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Confirm the script is loading<\/h3>\n\n\n\n<p>Open browser DevTools, go to the Network tab, and filter for <code>banner.js<\/code> or your npm bundle. Confirm the request completes with a 200 status and no console errors.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. Verify the banner renders<\/h3>\n\n\n\n<p>With the SDK loaded, the banner should appear according to your trigger configuration. If <code>showDelay<\/code> is 0 and <code>scrollTrigger<\/code> is 0, it should appear immediately. Check the Elements tab for a <code>.tolk-banner<\/code> element in the DOM.<\/p>\n\n\n\n<p>If the banner does not appear, check:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Is the API key correct?<\/li>\n<li>Is the Appspace ID correct?<\/li>\n<li>Is the Appspace configured with at least one banner in the dashboard?<\/li>\n<li>Are there any console errors?<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. Test on both iOS and Android<\/h3>\n\n\n\n<p>The banner behavior differs slightly between platforms. On iOS, the banner detects whether the app is installed (where possible via Safari&#39;s universal link behavior) and adjusts the CTA accordingly. On Android, the detection mechanism is different. Test on real devices or in Chrome DevTools with device emulation set to iPhone and Android Pixel modes.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. Verify deep link behavior<\/h3>\n\n\n\n<p>Tap the banner CTA. On a device with the app installed, confirm the app opens to the correct screen. On a device without the app installed, confirm the App Store or Google Play opens to the correct app listing.<\/p>\n\n\n\n<p>If you have deferred deep linking configured, the full test requires installing the app after tapping the banner and verifying the app opens to the expected content. This cannot be fully tested in a simulator; it requires a real device without the app installed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">5. Test dismissal and session cap<\/h3>\n\n\n\n<p>Dismiss the banner and refresh the page. The banner should not reappear within the <code>dismissalTtl<\/code> window. Open a new browser tab (new session) and the banner should appear again (up to the <code>sessionCap<\/code>).<\/p>\n\n\n\n<p>To reset dismissal state during development without waiting for the TTL to expire:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ In the browser console\nlocalStorage.removeItem(&#39;tolk_banner_dismissed&#39;);\nsessionStorage.removeItem(&#39;tolk_session_count&#39;);\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">6. Measure CLS impact<\/h3>\n\n\n\n<p>Use <a href=\"https:\/\/developer.chrome.com\/docs\/devtools\/performance\/\" rel=\"nofollow noopener\" target=\"_blank\">Chrome DevTools&#39; Performance panel<\/a> or <a href=\"https:\/\/pagespeed.web.dev\/\" rel=\"nofollow noopener\" target=\"_blank\">PageSpeed Insights<\/a> to check CLS before and after adding the banner. If CLS increased, the banner injection is causing layout shifts. See the <a href=\"https:\/\/tolinku.com\/blog\/smart-banners-and-seo\/\">smart banners and SEO guide<\/a> for remediation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Deployment Checklist<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><input disabled=\"\" type=\"checkbox\"> API key is the publishable key (<code>tolk_pub_...<\/code>), not the secret key<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Script is loaded with <code>async<\/code> or <code>defer<\/code><\/li>\n<li><input disabled=\"\" type=\"checkbox\"> <code>sessionCap<\/code> and <code>dismissalTtl<\/code> are set appropriately<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Banner appears correctly on both iOS and Android<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Deep links route to the correct app screen<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> CLS impact has been measured and is acceptable<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Event callbacks are connected to your analytics system<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Banner is not visible to users who already have the app installed (or shows an &quot;Open&quot; prompt instead)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n","protected":false},"excerpt":{"rendered":"<p>A practical tutorial for developers adding Tolinku smart banners to a web project. Covers npm install, script tag setup, configuration options, customization, event callbacks, and testing.<\/p>\n","protected":false},"author":2,"featured_media":668,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Integrating Smart Banners with the Tolinku Web SDK","rank_math_description":"Step-by-step guide to integrating Tolinku smart banners via npm or script tag. Covers configuration, customization API, event callbacks, and testing methods.","rank_math_focus_keyword":"smart banner SDK","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-banner-sdk-integration.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-banner-sdk-integration.png","footnotes":""},"categories":[1],"tags":[141,71,30,40,41],"class_list":["post-669","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-developer","tag-javascript","tag-sdks","tag-smart-banners","tag-web-to-app"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/669","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=669"}],"version-history":[{"count":1,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/669\/revisions"}],"predecessor-version":[{"id":670,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/669\/revisions\/670"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/668"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=669"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=669"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=669"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}