Free Shopify mobile app banner.
A copy-paste section that adds an app-install banner to your Shopify store. It shows each mobile shopper the right store button, works on any theme, and costs nothing.
Built by the Vunda team · Free to use · Updated 19 June 2026
See it in action
This is the section rendered live, with Vunda's app icon. It shows the App Store button on iPhone and Google Play on Android - the same behaviour your shoppers get.
A live preview. iPhone shoppers see the App Store button; Android shoppers see Google Play.
What is a smart app banner?
A smart app banner is the slim, dismissible bar at the top of a mobile store that invites shoppers to open it in your app. It detects the device and shows the matching button: the App Store on an iPhone, Google Play on Android. Each shopper sees one clear action, and it remembers a dismissal so it does not nag.
Why add an app banner to your Shopify store?
Because apps convert, but only if shoppers install them. Native shopping apps convert at roughly 3x the rate of mobile web (Criteo), and mobile was about 57% of global retail ecommerce in 2024 (Statista). A banner is the cheapest, highest-intent place to ask: it reaches people in the exact moment they are browsing on their phone. If you want the bigger picture first, read how to turn your Shopify store into a mobile app.
Free vs paid smart app banner apps
Most "smart app banner" and "mobile app banner" tools in the Shopify App Store are paid monthly apps. This snippet does the core job - device detection, the right store button, dismiss-and-remember - for free, with no app and no subscription. The trade-off is that you paste it in yourself (a two-minute job) and there is no vendor dashboard. You also keep full control of the markup.
How to add the smart app banner to your theme
- In your Shopify admin, go to Online Store, Themes, then the three-dot menu and Edit code.
- Under Sections, click Add a new section, name it
smart-app-banner, and replace the contents with the code below. - Save, then open the theme editor, add the Mobile App Banner section, and paste your App Store and Google Play links.
- Set your app icon and colours. It shows only to mobile visitors, so preview on your phone.
{%- comment -%}
Mobile App Banner - a free, copy-paste Shopify section from Vunda (vunda.app).
Shows a dismissible "get our app" bar to mobile visitors, with the correct
store button (App Store on iOS, Google Play on Android). Built to drop into
ANY theme:
- all class names are namespaced (vapp-) to avoid collisions
- defensive CSS resets so theme button/link styles do not leak in
- the banner is moved to <body> on load, so a transformed or
overflow-hidden section wrapper can never trap the fixed element
- everything is configurable from the theme editor (no code edits)
- behaviour ships in a bundled javascript asset tag (deferred, loaded once
per section type); per-instance config is read from data- attributes
How to use: Online Store > Themes > Edit code > Sections > Add a new section,
paste this in, then add "Mobile App Banner" to your theme and set your links.
{%- endcomment -%}
<aside
class="vapp-banner"
id="vapp-banner-{{ section.id }}"
role="complementary"
aria-label="Mobile app download banner"
data-vapp-dismiss-days="{{ section.settings.dismiss_days | default: 30 }}"
hidden
>
<div class="vapp-banner__container">
<button class="vapp-banner__close" type="button" data-vapp-close aria-label="Close banner">
<svg width="12" height="12" viewBox="0 0 20 20" fill="none" aria-hidden="true">
<path d="M15 5L5 15M5 5L15 15" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</button>
<div class="vapp-banner__content">
{%- if section.settings.app_icon -%}
<span class="vapp-banner__icon">
<img
src="{{ section.settings.app_icon | image_url: width: 112 }}"
srcset="{{ section.settings.app_icon | image_url: width: 56 }} 1x, {{ section.settings.app_icon | image_url: width: 112 }} 2x"
alt="{{ section.settings.heading | default: shop.name | escape }}"
width="56" height="56" loading="eager" decoding="async"
>
</span>
{%- endif -%}
<span class="vapp-banner__text">
{%- if section.settings.heading != blank -%}
<span class="vapp-banner__heading" role="heading" aria-level="2">{{ section.settings.heading }}</span>
{%- endif -%}
{%- if section.settings.text != blank -%}
<span class="vapp-banner__description">{{ section.settings.text }}</span>
{%- endif -%}
{%- comment -%}
Optional credit link - it keeps this section free. To hide it: untick
"Show Powered by Vunda credit" in the theme editor, or delete this block.
{%- endcomment -%}
{%- if section.settings.show_credit -%}
<a
class="vapp-banner__credit"
href="https://www.vunda.app/?utm_source=app-banner&utm_medium=widget&utm_campaign=free-tool"
target="_blank"
rel="nofollow noopener"
>Powered by Vunda</a>
{%- endif -%}
</span>
</div>
<div class="vapp-banner__actions">
{%- if section.settings.ios_url != blank -%}
<a
class="vapp-banner__button"
data-vapp-platform="ios"
href="{{ section.settings.ios_url }}"
rel="noopener noreferrer"
target="_blank"
>{{ section.settings.ios_text | default: 'App Store' }}</a>
{%- endif -%}
{%- if section.settings.android_url != blank -%}
<a
class="vapp-banner__button"
data-vapp-platform="android"
href="{{ section.settings.android_url }}"
rel="noopener noreferrer"
target="_blank"
>{{ section.settings.android_text | default: 'Google Play' }}</a>
{%- endif -%}
</div>
</div>
</aside>
{%- comment -%}
Per-instance colours and id need Liquid, and Liquid is NOT evaluated inside the
stylesheet asset tag - so, per Shopify's own guidance, instance-specific
styling stays in an inline style block.
{%- endcomment -%}
<style>
#vapp-banner-{{ section.id }} {
--vapp-bg: {{ section.settings.background_color | default: '#ffffff' }};
--vapp-button: {{ section.settings.button_color | default: '#000000' }};
--vapp-button-text: {{ section.settings.button_text_color | default: '#ffffff' }};
--vapp-text: {{ section.settings.text_color | default: '#1a1a1a' }};
}
/* Reset inside the banner so the host theme's a/button/img/p styles cannot leak in. */
.vapp-banner,
.vapp-banner *,
.vapp-banner *::before,
.vapp-banner *::after { box-sizing: border-box; }
.vapp-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 2147483000;
margin: 0;
background-color: var(--vapp-bg);
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.1);
font-family: inherit;
padding-top: env(safe-area-inset-top);
}
.vapp-banner[hidden] { display: none; }
.vapp-banner.vapp-is-open { animation: vapp-slide-down 0.3s ease-out; }
.vapp-banner.vapp-is-closing { animation: vapp-slide-up 0.3s ease-out forwards; }
@keyframes vapp-slide-down { from { transform: translateY(-100%); } to { transform: translateY(0); } }
@keyframes vapp-slide-up { to { transform: translateY(-100%); } }
.vapp-banner__container {
display: flex;
align-items: center;
gap: 0.875rem;
max-width: 100%;
margin: 0;
padding: 0.75rem 1rem;
}
.vapp-banner__close {
order: 3;
flex-shrink: 0;
-webkit-appearance: none;
appearance: none;
margin: 0;
padding: 0.5rem;
border: 0;
background: transparent;
color: var(--vapp-text);
line-height: 0;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: background-color 0.2s ease;
-webkit-tap-highlight-color: transparent;
}
.vapp-banner__close:hover,
.vapp-banner__close:focus-visible { background-color: rgba(0, 0, 0, 0.08); }
.vapp-banner__close svg { display: block; }
.vapp-banner__content {
display: flex;
align-items: center;
gap: 0.75rem;
min-width: 0;
flex: 1 1 auto;
}
.vapp-banner__icon { flex-shrink: 0; line-height: 0; }
.vapp-banner__icon img {
display: block;
width: 56px;
height: 56px;
max-width: none;
border-radius: 14px;
object-fit: cover;
}
.vapp-banner__text { display: flex; flex-direction: column; min-width: 0; }
.vapp-banner__heading {
margin: 0;
font-size: 0.9375rem;
font-weight: 600;
line-height: 1.3;
color: var(--vapp-text);
}
.vapp-banner__description {
margin: 0.125rem 0 0;
font-size: 0.8125rem;
line-height: 1.4;
color: var(--vapp-text);
opacity: 0.8;
}
.vapp-banner__credit {
display: inline-block;
margin-top: 0.25rem;
font-size: 0.6875rem;
line-height: 1;
color: var(--vapp-text);
opacity: 0.55;
text-decoration: none;
}
.vapp-banner__credit:hover,
.vapp-banner__credit:focus-visible { opacity: 0.85; text-decoration: underline; }
.vapp-banner__actions { display: flex; flex-direction: column; gap: 0.5rem; flex-shrink: 0; }
.vapp-banner__button {
-webkit-appearance: none;
appearance: none;
display: inline-flex;
align-items: center;
justify-content: center;
width: auto;
margin: 0;
padding: 0.5rem 1rem;
border: 0;
border-radius: 8px;
background-color: var(--vapp-button);
color: var(--vapp-button-text);
font-family: inherit;
font-size: 0.8125rem;
font-weight: 600;
line-height: 1.2;
letter-spacing: 0;
text-transform: none;
text-decoration: none;
white-space: nowrap;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.vapp-banner__button:hover,
.vapp-banner__button:focus-visible {
color: var(--vapp-button-text);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.vapp-banner__button:active { transform: translateY(0); }
@media (max-width: 359px) {
.vapp-banner__container { gap: 0.5rem; padding: 0.625rem 0.75rem; }
.vapp-banner__icon img { width: 44px; height: 44px; }
.vapp-banner__button { padding: 0.4375rem 0.75rem; }
}
/* An app-install banner is a mobile pattern; hide it from tablet/desktop up. */
@media (min-width: 768px) { .vapp-banner { display: none !important; } }
@media (prefers-reduced-motion: reduce) {
.vapp-banner.vapp-is-open,
.vapp-banner.vapp-is-closing { animation: none; }
}
</style>
{% javascript %}
(function () {
function initBanner(banner) {
var dismissDays = parseInt(banner.getAttribute('data-vapp-dismiss-days'), 10) || 30;
var STORAGE_KEY = 'vapp-banner-dismissed-' + banner.id;
function isMobile() {
var hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
return hasTouch && window.matchMedia('(max-width: 767px)').matches;
}
function detectPlatform() {
var ua = navigator.userAgent || navigator.vendor || '';
if (/iPad|iPhone|iPod/.test(ua) && !window.MSStream) return 'ios';
if (/android/i.test(ua)) return 'android';
return null;
}
function isDismissed() {
try {
var raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return false;
if (Date.now() > JSON.parse(raw).expiry) {
localStorage.removeItem(STORAGE_KEY);
return false;
}
return true;
} catch (e) {
return false;
}
}
function setDismissed() {
try {
localStorage.setItem(
STORAGE_KEY,
JSON.stringify({ expiry: Date.now() + dismissDays * 86400000 })
);
} catch (e) {}
}
if (isDismissed() || !isMobile()) return;
var platform = detectPlatform();
if (!platform) return;
var target = banner.querySelector('.vapp-banner__button[data-vapp-platform="' + platform + '"]');
if (!target) return; // no app link configured for this visitor's platform
var buttons = banner.querySelectorAll('.vapp-banner__button');
for (var i = 0; i < buttons.length; i++) {
if (buttons[i].getAttribute('data-vapp-platform') !== platform) {
buttons[i].style.display = 'none';
}
}
// Escape any transformed / overflow-hidden section wrapper so position:fixed works.
if (banner.parentNode !== document.body) document.body.appendChild(banner);
banner.hidden = false;
banner.classList.add('vapp-is-open');
var closeBtn = banner.querySelector('[data-vapp-close]');
if (closeBtn) {
closeBtn.addEventListener('click', function () {
banner.classList.remove('vapp-is-open');
banner.classList.add('vapp-is-closing');
setDismissed();
setTimeout(function () { banner.remove(); }, 300);
});
}
target.addEventListener('click', setDismissed);
}
// This script is bundled and deferred (one copy per section type), so the DOM is
// ready and a single pass initialises every Mobile App Banner on the page.
var banners = document.querySelectorAll('.vapp-banner');
for (var b = 0; b < banners.length; b++) initBanner(banners[b]);
})();
{% endjavascript %}
{% schema %}
{
"name": "Mobile App Banner",
"tag": "section",
"settings": [
{ "type": "header", "content": "App links" },
{ "type": "image_picker", "id": "app_icon", "label": "App icon (square, e.g. 512x512)" },
{ "type": "url", "id": "ios_url", "label": "App Store (iOS) link" },
{ "type": "url", "id": "android_url", "label": "Google Play (Android) link" },
{ "type": "header", "content": "Copy" },
{ "type": "text", "id": "heading", "label": "Heading", "default": "Get our app" },
{ "type": "text", "id": "text", "label": "Subtext", "default": "Shop faster with app-only deals and instant order updates." },
{ "type": "text", "id": "ios_text", "label": "iOS button text", "default": "App Store", "visible_if": "{{ section.settings.ios_url != blank }}" },
{ "type": "text", "id": "android_text", "label": "Android button text", "default": "Google Play", "visible_if": "{{ section.settings.android_url != blank }}" },
{ "type": "header", "content": "Appearance" },
{ "type": "color", "id": "background_color", "label": "Background", "default": "#ffffff" },
{ "type": "color", "id": "text_color", "label": "Text", "default": "#1a1a1a" },
{ "type": "color", "id": "button_color", "label": "Button", "default": "#000000" },
{ "type": "color", "id": "button_text_color", "label": "Button text", "default": "#ffffff" },
{ "type": "header", "content": "Behaviour" },
{ "type": "range", "id": "dismiss_days", "min": 1, "max": 90, "step": 1, "unit": "d", "label": "Stay hidden after dismiss", "default": 30 },
{ "type": "header", "content": "Credit" },
{ "type": "checkbox", "id": "show_credit", "label": "Show \"Powered by Vunda\" credit", "default": true, "info": "Keeps this free section free. Untick to remove the small credit link." }
],
"presets": [
{ "name": "Mobile App Banner" }
]
}
{% endschema %}
What you can customise
- App icon, heading and subtext
- App Store and Google Play links (only the matching one shows per device)
- Button, background, text and button-text colours
- How many days it stays hidden after a shopper dismisses it
- The "Powered by Vunda" credit (on by default - one tick to remove)
Removing the Vunda credit
The section adds a small "Powered by Vunda" link, which is how it stays free to use. To remove it, untick Show "Powered by Vunda" credit in the theme editor, or delete the clearly marked credit block in the code. No catch - the banner works exactly the same without it.
Will it work on my theme?
Yes - it is built theme-agnostic. The class names are namespaced so they will not clash, the styles reset themselves so your theme's button and link styles will not distort it, and on load the banner moves itself to the end of the page body so it is never trapped inside a transformed section wrapper - the usual reason a fixed banner breaks on non-Dawn themes. It was tested on Dawn and built to drop into others. Still weighing a full app? Compare the main Shopify app builders.
Frequently asked questions
Is the smart app banner really free?
Yes. It is a copy-paste Shopify section with no monthly fee and no app to install. You own the code and can edit it freely. Most equivalent "smart app banner" tools in the Shopify App Store charge a recurring subscription.
Does it work on any Shopify theme?
It is built to. Every class name is namespaced so it will not clash with your theme, the styles reset themselves so theme button styles will not distort it, and on load the banner moves itself to the end of the page body so it is never trapped inside a section wrapper. It was tested on Dawn.
Does the banner show on desktop?
No. An app-install banner is a mobile pattern, so it is hidden from 768px wide and up, and only appears to touch devices running iOS or Android.
I do not have an app yet. How do I get one?
You need an iOS or Android app to link the banner to. That is what Vunda does: it turns your Shopify store into a real native app you publish under your own developer accounts, and it is free to build. See the full guide to turning your Shopify store into a mobile app.
Can I show it to only iPhone or only Android users?
Yes. Set only the link for the platform you have. The banner shows a button only when the visitor's device matches a link you provided, and stays hidden otherwise.
Sources
- Criteo, "Retail and travel apps see higher conversions than mobile web", retrieved 2026-06-19, https://www.criteo.com/blog/retail-travel-apps-higher-conversions-mobile/
- Statista, "Mobile commerce worldwide: statistics & facts", retrieved 2026-06-19, https://www.statista.com/topics/11883/mobile-commerce-worldwide/