{"id":1340,"date":"2026-06-05T17:00:00","date_gmt":"2026-06-05T22:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=1340"},"modified":"2026-03-07T03:35:08","modified_gmt":"2026-03-07T08:35:08","slug":"banner-dismiss-behavior","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/banner-dismiss-behavior\/","title":{"rendered":"Smart Banner Dismiss Behavior: UX Best Practices"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">A user dismisses your smart banner. What happens next? If the banner reappears on the next page load, you have annoyed the user. If it never appears again, you have lost a conversion opportunity. The dismiss behavior, how long the banner stays hidden after dismissal and under what conditions it reappears, is one of the most important UX decisions for smart banners.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide covers dismiss persistence strategies, frequency capping, re-engagement patterns, and the technical implementation of dismiss behavior. For the complete smart banners setup, see the <a href=\"https:\/\/tolinku.com\/blog\/smart-app-banners-complete-guide\/\">smart banners guide<\/a>. For scheduling banners, see <a href=\"https:\/\/tolinku.com\/blog\/banner-scheduling-campaigns\/\">banner scheduling for campaigns<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/screenshot-banner-form-preview-1772822964529.png\" alt=\"Tolinku smart banner live preview in a mobile device frame\">\n<em>The live banner preview showing how the banner appears on a mobile device.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Dismiss Decision<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">When a user taps the close button (X) on your banner, they are communicating one of several things:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>&quot;Not now.&quot;<\/strong> They are busy with something else. They might be interested later.<\/li>\n<li><strong>&quot;I already have the app.&quot;<\/strong> The banner is irrelevant because they have already installed it.<\/li>\n<li><strong>&quot;I don&#39;t want the app.&quot;<\/strong> They have decided not to install it.<\/li>\n<li><strong>&quot;This is in my way.&quot;<\/strong> The banner is blocking content they want to see.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">The challenge: you do not know which one. Your dismiss behavior should accommodate all four by hiding the banner for a reasonable period and then testing whether the user&#39;s situation has changed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Dismiss Persistence Strategies<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Strategy 1: Session-Based Dismiss<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The banner is hidden for the current browsing session. It reappears when the user returns in a new session (new tab, next day, etc.).<\/p>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ Session storage: cleared when tab closes\nfunction dismissBanner() {\n  sessionStorage.setItem(&#39;banner_dismissed&#39;, &#39;true&#39;);\n  hideBanner();\n}\n\nfunction shouldShowBanner() {\n  return sessionStorage.getItem(&#39;banner_dismissed&#39;) !== &#39;true&#39;;\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>When to use:<\/strong> For banners with time-sensitive content (flash sales, live events) where you want maximum visibility across visits.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Risk:<\/strong> Too aggressive. Users who dismiss on every visit will become frustrated.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Strategy 2: Time-Based Dismiss<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The banner is hidden for a fixed period (e.g., 7 days) after dismissal. After the period, it reappears.<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function dismissBanner() {\n  const dismissUntil = Date.now() + (7 * 24 * 60 * 60 * 1000); \/\/ 7 days\n  localStorage.setItem(&#39;banner_dismiss_until&#39;, dismissUntil.toString());\n  hideBanner();\n}\n\nfunction shouldShowBanner() {\n  const dismissUntil = localStorage.getItem(&#39;banner_dismiss_until&#39;);\n  if (dismissUntil &amp;&amp; Date.now() &lt; parseInt(dismissUntil)) {\n    return false;\n  }\n  return true;\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>When to use:<\/strong> The default choice for most smart banners. 7 days is a common interval. Shorter (1-3 days) for high-intent campaigns; longer (14-30 days) for less urgent banners.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Strategy 3: Progressive Backoff<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Each dismissal increases the hidden period. The first dismiss hides for 1 day, the second for 7 days, the third for 30 days, and the fourth hides permanently.<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function dismissBanner() {\n  const dismissCount = parseInt(localStorage.getItem(&#39;banner_dismiss_count&#39;) || &#39;0&#39;) + 1;\n  localStorage.setItem(&#39;banner_dismiss_count&#39;, dismissCount.toString());\n\n  let hideForMs;\n  switch (dismissCount) {\n    case 1: hideForMs = 1 * 24 * 60 * 60 * 1000; break;  \/\/ 1 day\n    case 2: hideForMs = 7 * 24 * 60 * 60 * 1000; break;  \/\/ 7 days\n    case 3: hideForMs = 30 * 24 * 60 * 60 * 1000; break; \/\/ 30 days\n    default: hideForMs = 365 * 24 * 60 * 60 * 1000; break; \/\/ Effectively permanent\n  }\n\n  const dismissUntil = Date.now() + hideForMs;\n  localStorage.setItem(&#39;banner_dismiss_until&#39;, dismissUntil.toString());\n  hideBanner();\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>When to use:<\/strong> When you want to respect persistent dismissers while still re-engaging users who may have changed their mind. This is the recommended approach for most applications.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Strategy 4: Permanent Dismiss<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Once dismissed, the banner never appears again (or until the user clears their browser data).<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function dismissBanner() {\n  localStorage.setItem(&#39;banner_dismissed_permanently&#39;, &#39;true&#39;);\n  hideBanner();\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>When to use:<\/strong> For banners that users find particularly intrusive (full-screen interstitials, audio\/video banners). A single dismiss should be final.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Risk:<\/strong> You lose all future conversion opportunities from that user.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cookie vs. localStorage<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">localStorage<\/h3>\n\n\n\n<pre><code class=\"language-javascript\">localStorage.setItem(&#39;banner_dismiss_until&#39;, timestamp);\n<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Persists across sessions and page loads.<\/li>\n<li>No size limit concerns for this use case.<\/li>\n<li>Not sent to the server on every request (unlike cookies).<\/li>\n<li>Cleared when the user clears site data.<\/li>\n<li>Partitioned per origin (domain + protocol).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Cookies<\/h3>\n\n\n\n<pre><code class=\"language-javascript\">document.cookie = `banner_dismissed=1; max-age=${7 * 86400}; path=\/; SameSite=Lax`;\n<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Sent to the server on every request (useful if you want server-side rendering decisions).<\/li>\n<li>Can be set with an explicit expiration date.<\/li>\n<li>Subject to cookie consent requirements under GDPR\/ePrivacy.<\/li>\n<li>Limited to ~4KB.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Recommendation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use <strong>localStorage<\/strong> for client-side banner dismiss tracking. It is simpler, not subject to cookie consent requirements (since it is not a tracking cookie), and does not add overhead to HTTP requests.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If your smart banner is rendered server-side (SSR), use a cookie so the server knows whether to include the banner in the initial HTML response. This avoids a flash of the banner appearing and then disappearing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Frequency Capping<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Impression-Based Caps<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Limit how many times a user sees the banner before it auto-hides:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function trackImpression() {\n  const count = parseInt(localStorage.getItem(&#39;banner_impressions&#39;) || &#39;0&#39;) + 1;\n  localStorage.setItem(&#39;banner_impressions&#39;, count.toString());\n  return count;\n}\n\nfunction shouldShowBanner() {\n  const impressions = parseInt(localStorage.getItem(&#39;banner_impressions&#39;) || &#39;0&#39;);\n  const maxImpressions = 10; \/\/ Show up to 10 times, then hide\n\n  if (impressions &gt;= maxImpressions) {\n    return false;\n  }\n  return true;\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Typical caps:<\/strong> 5-15 impressions. After this, if the user has not engaged, the banner is unlikely to convert them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Page-Based Caps<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Only show the banner on certain pages or after a certain number of page views:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function shouldShowBanner() {\n  const pageViews = parseInt(sessionStorage.getItem(&#39;page_views&#39;) || &#39;0&#39;);\n\n  \/\/ Don&#39;t show on the first page (let user orient themselves)\n  if (pageViews &lt; 2) return false;\n\n  \/\/ Don&#39;t show after 5 pages in a session (user is engaged with web content)\n  if (pageViews &gt; 5) return false;\n\n  return true;\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This pattern avoids interrupting users who just arrived (first page) and users who are deeply engaged with your web content (5+ pages).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Combination Caps<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Combine multiple signals:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function shouldShowBanner() {\n  \/\/ Check dismiss persistence\n  const dismissUntil = localStorage.getItem(&#39;banner_dismiss_until&#39;);\n  if (dismissUntil &amp;&amp; Date.now() &lt; parseInt(dismissUntil)) return false;\n\n  \/\/ Check impression cap\n  const impressions = parseInt(localStorage.getItem(&#39;banner_impressions&#39;) || &#39;0&#39;);\n  if (impressions &gt;= 15) return false;\n\n  \/\/ Check page view threshold\n  const pageViews = parseInt(sessionStorage.getItem(&#39;page_views&#39;) || &#39;0&#39;);\n  if (pageViews &lt; 2) return false;\n\n  \/\/ Check if user already has the app (via detection or flag)\n  if (localStorage.getItem(&#39;has_app&#39;) === &#39;true&#39;) return false;\n\n  return true;\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Re-Engagement Triggers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After the dismiss period expires, do not just show the same banner. Use context-aware re-engagement:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">New Campaign Trigger<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When a new marketing campaign starts, show a fresh banner even if the user dismissed the previous one:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function shouldShowBanner(config) {\n  const lastDismissedCampaign = localStorage.getItem(&#39;dismissed_campaign_id&#39;);\n\n  \/\/ If this is a new campaign, reset the dismiss state\n  if (config.campaignId !== lastDismissedCampaign) {\n    return true;\n  }\n\n  \/\/ Otherwise, check the dismiss timer\n  return checkDismissTimer();\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Content-Specific Trigger<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Show the banner when the user visits a page with high-value content that is better in the app:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function shouldShowBanner() {\n  const currentPage = window.location.pathname;\n  const highValuePages = [&#39;\/products\/&#39;, &#39;\/checkout&#39;, &#39;\/account\/&#39;];\n\n  \/\/ On high-value pages, show the banner even if it was recently dismissed\n  \/\/ (but still respect permanent dismissals)\n  if (highValuePages.some(p =&gt; currentPage.startsWith(p))) {\n    return checkPermanentDismiss() === false;\n  }\n\n  return checkDismissTimer();\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Platform-Specific Trigger<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If you detect the user is on mobile (where the app provides the most value), show the banner more aggressively than on desktop:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function getDismissDuration() {\n  const isMobile = \/Android|iPhone|iPad\/.test(navigator.userAgent);\n  return isMobile ? 3 * 24 * 60 * 60 * 1000 : 14 * 24 * 60 * 60 * 1000;\n  \/\/ 3 days on mobile, 14 days on desktop\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Tracking Dismiss Events<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Track dismissals to understand how users interact with your banners:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function dismissBanner(reason) {\n  \/\/ Track the dismiss event\n  analytics.track(&#39;banner_dismissed&#39;, {\n    bannerId: currentBanner.id,\n    campaignId: currentBanner.campaignId,\n    reason: reason, \/\/ &#39;close_button&#39;, &#39;swipe&#39;, &#39;timeout&#39;\n    impressionCount: parseInt(localStorage.getItem(&#39;banner_impressions&#39;) || &#39;0&#39;),\n    pageUrl: window.location.href,\n    dismissCount: parseInt(localStorage.getItem(&#39;banner_dismiss_count&#39;) || &#39;0&#39;) + 1,\n  });\n\n  \/\/ Persist the dismiss\n  persistDismiss();\n  hideBanner();\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Monitor:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Dismiss rate:<\/strong> Percentage of impressions that result in a dismiss. High dismiss rates (&gt;50%) suggest the banner is too intrusive.<\/li>\n<li><strong>Dismiss-to-install ratio:<\/strong> Some users dismiss the banner but install later. Track whether dismissed users eventually convert.<\/li>\n<li><strong>Time to dismiss:<\/strong> How quickly users dismiss. Instant dismisses (&lt;1 second) suggest the banner is blocking content. Longer times suggest the user read the message.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Tolinku Dismiss Configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/tolinku.com\/features\/smart-banners\">Tolinku&#39;s smart banners<\/a> provide configurable dismiss behavior through the dashboard. Set dismiss duration, frequency caps, and re-engagement rules without code changes. The banner SDK handles persistence via localStorage and tracks dismiss events automatically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For banner display behavior configuration, see the <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/smart-banners\/display-behavior\/\">Tolinku smart banner documentation<\/a>. For scheduling, see <a href=\"https:\/\/tolinku.com\/blog\/banner-scheduling-campaigns\/\">banner scheduling for campaigns<\/a>. For the complete smart banners setup, see the <a href=\"https:\/\/tolinku.com\/blog\/smart-app-banners-complete-guide\/\">smart banners guide<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Handle banner dismissals gracefully. Set the right frequency caps, respect user preferences, use cookie and localStorage persistence, and re-engage without annoying users.<\/p>\n","protected":false},"author":2,"featured_media":1339,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Smart Banner Dismiss Behavior: UX Best Practices","rank_math_description":"Handle banner dismissals gracefully. Set the right frequency caps, respect user preferences, and re-engage without being annoying.","rank_math_focus_keyword":"banner dismiss behavior","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-dismiss-behavior.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-dismiss-behavior.png","footnotes":""},"categories":[16],"tags":[254,39,110,69,40,33,155,41],"class_list":["post-1340","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-marketing","tag-best-practices","tag-conversion","tag-marketing","tag-mobile-development","tag-smart-banners","tag-user-experience","tag-ux-design","tag-web-to-app"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1340","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=1340"}],"version-history":[{"count":3,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1340\/revisions"}],"predecessor-version":[{"id":2285,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1340\/revisions\/2285"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/1339"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=1340"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=1340"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=1340"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}