Exit Intent Popups
Exit Intent popups detect when users are about to leave your page and display targeted offers to keep them engaged. Supports both simple image popups and fully customizable HTML templates.
Basic Usage
Section titled “Basic Usage”// Wait for SDK to be fully initializedwindow.addEventListener('next:initialized', function() { console.log('SDK initialized, setting up exit intent...');
// Simple image popup setup next.exitIntent({ image: 'https://example.com/exit-popup.webp', action: async () => { const result = await next.applyCoupon('SAVE10'); if (result.success) { alert('Coupon applied: ' + result.message); } } });
// Optional: Listen to events for analytics next.on('exit-intent:shown', (data) => { console.log('Exit intent shown:', data); });});<!-- Define your template in HTML --><template data-template="exit-intent"> <div class="exit-modal"> <h2>Wait! Don't Leave Yet!</h2> <p>Get 20% OFF your order</p>
<!-- Display dynamic cart data --> <div class="cart-info"> Your cart total: <span data-next-display="cart.total"></span> </div>
<!-- Action buttons with special attributes --> <button data-exit-intent-action="apply-coupon" data-coupon-code="SAVE20"> Apply 20% Discount </button>
<button data-exit-intent-action="close"> No Thanks </button> </div></template>
<script>window.addEventListener('next:initialized', function() { next.exitIntent({ template: 'exit-intent', // Name matches data-template attribute showCloseButton: true, // Show X button overlayClosable: true, // Click overlay to close maxTriggers: 1, // Show only once per session disableOnMobile: true // Desktop only by default });});</script>Configuration Options
Section titled “Configuration Options”Complete Parameter Reference
Section titled “Complete Parameter Reference”| Parameter | Type | Description | Default |
|---|---|---|---|
image | string | URL of popup image | - |
template | string | Name of template (matches data-template attribute) | - |
action | function | Function to execute on click/custom action | - |
showCloseButton | boolean | Show X button on modal | false |
overlayClosable | boolean | Allow clicking overlay to close | true |
maxTriggers | number | Maximum times to show per session | 1 |
disableOnMobile | boolean | Disable on mobile devices | true |
mobileScrollTrigger | boolean | Enable scroll trigger on mobile (50% scroll) | false |
useSessionStorage | boolean | Remember dismissal in session | true |
sessionStorageKey | string | Custom storage key | 'exit-intent-dismissed' |
imageClickable | boolean | Make image clickable to trigger action | true |
actionButtonText | string | Text for action button (shows button instead of clickable image) | - |
Simple Examples
Section titled “Simple Examples”// Display popup without any actionfunction justShowPopup() { next.exitIntent({ image: 'https://example.com/just-popup.webp' });}No action function needed - just shows the image popup.
// Redirect to offer page when clickedfunction redirectExample() { next.exitIntent({ image: 'https://example.com/special-offer.webp', action: () => { window.location.href = '/special-offer'; } });}// Different popups based on cart statefunction conditionalExample() { const cartCount = next.getCartCount();
if (cartCount === 0) { next.exitIntent({ image: 'https://example.com/empty-cart.webp', action: () => window.location.href = '/products' }); } else { next.exitIntent({ image: 'https://example.com/discount.webp', action: () => next.applyCoupon('SAVE10') }); }}// Show a button instead of clickable imagefunction actionButtonExample() { next.exitIntent({ image: 'https://example.com/offer.webp', actionButtonText: 'Claim My Discount', action: async () => { await next.applyCoupon('SAVE20'); alert('Discount applied!'); } });}When actionButtonText is provided, a button appears below the image.
Template-Based Popups
Section titled “Template-Based Popups”Template Action Attributes
Section titled “Template Action Attributes”When using templates, add special attributes to buttons and links to trigger actions:
| Attribute | Additional Attributes | Description |
|---|---|---|
data-exit-intent-action="close" | - | Closes the modal |
data-exit-intent-action="apply-coupon" | data-coupon-code="CODE" | Applies coupon and closes modal |
data-exit-intent-action="custom" | - | Triggers the custom action function |
Template Example
Section titled “Template Example”<template data-template="exit-intent"> <div class="exit-modal-content"> <!-- This content does nothing when clicked --> <h2>Special Offer!</h2> <p>Click the buttons below for actions:</p>
<!-- Only these buttons trigger actions --> <button data-exit-intent-action="apply-coupon" data-coupon-code="SAVE10"> Apply SAVE10 Coupon </button>
<button data-exit-intent-action="custom"> Custom Action </button>
<button data-exit-intent-action="close"> Close Modal </button> </div></template>
<script>next.exitIntent({ template: 'exit-intent', action: () => { // This ONLY runs when clicking elements with: // data-exit-intent-action="custom" console.log('Custom action triggered!'); alert('Custom action!'); }});</script>Multiple Offer Template
Section titled “Multiple Offer Template”<template data-template="exit-intent-offers"> <div class="exit-offers"> <h2>Choose Your Discount!</h2>
<div class="offer-grid"> <div class="offer"> <h3>10% OFF</h3> <button data-exit-intent-action="apply-coupon" data-coupon-code="SAVE10"> Apply Code </button> </div>
<div class="offer"> <h3>Free Shipping</h3> <button data-exit-intent-action="apply-coupon" data-coupon-code="FREESHIP"> Get Free Shipping </button> </div>
<div class="offer"> <h3>$5 OFF</h3> <button data-exit-intent-action="apply-coupon" data-coupon-code="FIVER"> Save $5 </button> </div> </div>
<button data-exit-intent-action="close" class="close-link"> I don't want to save money </button> </div></template>Events
Section titled “Events”The exit intent system emits several events for analytics and tracking:
next.on('exit-intent:shown', (data) => { // data.imageUrl - The image URL shown (if using image) // data.template - The template name (if using template)
console.log('Exit intent displayed:', data);
// Track with analytics gtag('event', 'exit_intent_shown', { type: data.template ? 'template' : 'image' });});Fired when popup is displayed.
next.on('exit-intent:clicked', (data) => { // data.imageUrl - The image URL clicked
console.log('Exit intent clicked:', data);});Fired when popup is clicked (image mode only, when imageClickable: true).
next.on('exit-intent:dismissed', (data) => { // data.imageUrl - The image URL (if using image) // data.template - The template name (if using template)
console.log('Exit intent dismissed:', data);});Fired when popup is closed without action (overlay click or ESC key).
next.on('exit-intent:closed', (data) => { // data.imageUrl or data.template
console.log('Exit intent close button clicked:', data);});Fired when close button (X) is clicked.
next.on('exit-intent:action', (data) => { // data.action - The action type ('close', 'apply-coupon', 'custom') // data.couponCode - The coupon code (if action is 'apply-coupon')
console.log('Exit intent action:', data.action);
if (data.action === 'apply-coupon') { console.log('Coupon applied:', data.couponCode); }});Fired when template action buttons are clicked.
Advanced Examples
Section titled “Advanced Examples”Page-Specific Strategies
Section titled “Page-Specific Strategies”// Different popups for different pageswindow.addEventListener('next:initialized', function() { const pathname = window.location.pathname;
if (pathname.includes('/product')) { // Product page - offer discount next.exitIntent({ image: '/images/10-percent-off.jpg', action: () => next.applyCoupon('SAVE10') }); } else if (pathname.includes('/cart')) { // Cart page - free shipping offer with template next.exitIntent({ template: 'exit-intent-shipping', showCloseButton: true }); } else { // Other pages - newsletter signup next.exitIntent({ template: 'exit-intent-newsletter', overlayClosable: true }); }});Cart Value-Based Offers
Section titled “Cart Value-Based Offers”function setupDynamicExitIntent() { const cartData = next.getCartData(); const cartTotal = cartData?.totals?.total?.value || 0;
if (cartTotal === 0) { // Empty cart - show bestsellers next.exitIntent({ image: '/images/bestsellers.jpg', action: () => window.location.href = '/bestsellers' }); } else if (cartTotal < 50) { // Small cart - offer percentage discount next.exitIntent({ template: 'exit-intent-discount-15', showCloseButton: true }); } else if (cartTotal < 100) { // Medium cart - offer free shipping next.exitIntent({ template: 'exit-intent-free-shipping', overlayClosable: true }); } else { // Large cart - offer free gift next.exitIntent({ image: '/images/free-gift-100.jpg', action: async () => { await next.addItem({ packageId: 99, quantity: 1 }); // Free gift } }); }}
// Call when cart changesnext.on('cart:updated', setupDynamicExitIntent);Time-Based Trigger
Section titled “Time-Based Trigger”// Show exit intent only after user has been on page for 30 secondslet exitIntentTimer;
window.addEventListener('next:initialized', function() { exitIntentTimer = setTimeout(() => { next.exitIntent({ image: '/images/dont-leave-yet.jpg', action: () => next.applyCoupon('COMEBACK'), maxTriggers: 1 }); }, 30000); // 30 seconds});
// Clear timer if user completes checkoutnext.on('checkout:started', () => { clearTimeout(exitIntentTimer);});Profile-Based Offers
Section titled “Profile-Based Offers”// Different offers based on user segmentfunction showProfileBasedExitIntent() { const activeProfile = next.getActiveProfile();
if (activeProfile === 'vip') { next.exitIntent({ template: 'exit-intent-vip', showCloseButton: true }); } else if (activeProfile === 'new-customer') { next.exitIntent({ image: '/images/first-time-discount.jpg', action: () => next.applyCoupon('WELCOME20') }); } else { next.exitIntent({ template: 'exit-intent-standard' }); }}Styling
Section titled “Styling”Customize the exit intent popup appearance with CSS:
/* Exit intent overlay */[data-exit-intent="overlay"] { background: rgba(0, 0, 0, 0.8) !important;}
/* Popup container */[data-exit-intent="popup"] { /* Styles are inline but can be overridden */}
/* Template popup specific */.exit-intent-template-popup { background: white; border-radius: 10px; padding: 20px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);}
/* Close button */[data-exit-intent="close"] { color: #666; font-size: 28px;}
[data-exit-intent="close"]:hover { color: #000;}/* Your custom template content */.exit-modal-content { padding: 40px; text-align: center; max-width: 500px;}
.exit-modal-content h2 { margin-bottom: 20px; font-size: 28px; color: #333;}
.exit-modal-content p { margin-bottom: 30px; font-size: 16px; color: #666;}
.exit-modal-content button { margin: 10px; padding: 12px 30px; font-size: 16px; border-radius: 5px; border: none; cursor: pointer; transition: all 0.3s ease;}
.exit-modal-content button:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);}/* Custom animation for popup */@keyframes exitIntentSlideIn { from { opacity: 0; transform: translate(-50%, -50%) scale(0.8) rotateX(-15deg); } to { opacity: 1; transform: translate(-50%, -50%) scale(1) rotateX(0deg); }}
[data-exit-intent="popup"] { animation: exitIntentSlideIn 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);}
/* Overlay fade in */@keyframes overlayFadeIn { from { opacity: 0; } to { opacity: 1; }}
[data-exit-intent="overlay"] { animation: overlayFadeIn 0.3s ease;}/* Mobile optimization */@media (max-width: 768px) { .exit-modal-content { padding: 20px; max-width: 90vw; }
.exit-modal-content h2 { font-size: 22px; }
.exit-modal-content button { width: 100%; margin: 5px 0; }
[data-exit-intent="close"] { font-size: 24px; top: 5px; right: 5px; }}Mobile Behavior
Section titled “Mobile Behavior”-
Desktop Detection (Default)
- Triggers when mouse leaves top of viewport (
clientY <= 10) - Uses
mouseoutevent ondocument.documentElement - Enabled by default
- Triggers when mouse leaves top of viewport (
-
Mobile Detection (Opt-in)
- Disabled by default (
disableOnMobile: true) - Can enable with
mobileScrollTrigger: true - Triggers at 50% scroll depth
- Detects mobile via: touch capability + (mobile width OR mobile user agent)
- Disabled by default (
-
Mobile Optimization
- Set
disableOnMobile: falseto enable on mobile - Add
mobileScrollTrigger: truefor scroll-based trigger - Test templates on small screens
- Consider simpler offers for mobile users
- Set
// Enable exit intent on mobile with scroll triggernext.exitIntent({ template: 'exit-intent-mobile', disableOnMobile: false, // Enable on mobile mobileScrollTrigger: true, // Use scroll detection maxTriggers: 1, // Show only once showCloseButton: true});Best Practices
Section titled “Best Practices”-
Choose the Right Method
- Image popups for quick, visual offers
- Templates for interactive, complex modals
- Templates allow dynamic cart data display
-
Session Management
- Use
maxTriggers: 1to avoid annoying users - Leverage
sessionStorageto remember dismissals - Different triggers for different pages
- Reset if user takes desired action
- Use
-
Mobile Optimization
- Default
disableOnMobile: truefor desktop-only - Enable
mobileScrollTriggersparingly - Test template layouts on small screens
- Simpler offers work better on mobile
- Default
-
Template Best Practices
- Keep templates hidden with
<template>tags - Use semantic
data-templatenames - Include cart display attributes for dynamic data
- Test all action buttons thoroughly
- Provide clear close options
- Keep templates hidden with
-
Performance
- Templates are only cloned when needed
- Images should be optimized and pre-loaded
- Use CDN for image hosting
- Lazy-load the exit intent enhancer
-
User Experience
- Don’t trigger immediately - wait for engagement
- Provide clear value in your offer
- Make close/dismiss obvious
- Track conversions to measure effectiveness
API Methods
Section titled “API Methods”next.exitIntent()
Section titled “next.exitIntent()”// Initialize exit intentnext.exitIntent(options);The main API method. Pass configuration options to set up the exit intent popup.
next.disableExitIntent()
Section titled “next.disableExitIntent()”// Disable exit intentnext.disableExitIntent();Disables the exit intent popup and removes event listeners.
// Disable when user starts checkoutnext.on('checkout:started', () => { next.disableExitIntent();});Integration Tips
Section titled “Integration Tips”// Track exit intent conversionnext.on('exit-intent:action', (data) => { gtag('event', 'exit_intent_conversion', { action_type: data.action, coupon_code: data.couponCode });});// Random A/B test for different templatesconst variant = Math.random() < 0.5 ? 'variant-a' : 'variant-b';
next.exitIntent({ template: `exit-intent-${variant}`, showCloseButton: true});
// Track which variantnext.on('exit-intent:shown', () => { gtag('event', 'exit_intent_variant', { variant: variant });});// Use cart data to customize offersconst cartData = next.getCartData();const hasSubscription = cartData.items.some(item => item.is_recurring);
if (hasSubscription) { next.exitIntent({ template: 'exit-intent-subscription-discount' });} else { next.exitIntent({ template: 'exit-intent-standard' });}// Wait for user engagement before enablinglet userEngaged = false;
document.addEventListener('scroll', function() { if (!userEngaged && window.scrollY > 200) { userEngaged = true; next.exitIntent({ image: '/images/exit-offer.jpg', action: () => next.applyCoupon('ENGAGED10') }); }}, { once: true });Troubleshooting
Section titled “Troubleshooting”Exit Intent Not Triggering
Section titled “Exit Intent Not Triggering”- Verify SDK is initialized (
window.nextexists) - Check if
disableOnMobile: trueis blocking on mobile device - Ensure either
imageortemplateis provided - Check console for errors
- Verify
maxTriggershasn’t been exceeded - Clear session storage to reset
Template Not Found
Section titled “Template Not Found”// Ensure template exists with matching name<template data-template="exit-intent"> <!-- content --></template>
// And matches in JavaScriptnext.exitIntent({ template: 'exit-intent' // Must match exactly});Actions Not Working
Section titled “Actions Not Working”- For image popups: Check
imageClickableistrue(default) - For templates: Ensure buttons have
data-exit-intent-actionattribute - Custom actions require
data-exit-intent-action="custom" - Apply coupon requires
data-coupon-codeattribute
Related Documentation
Section titled “Related Documentation”- FOMO Notifications - Another behavioral trigger feature
- Events API - Complete event reference
- Data Attributes - Using cart data in templates
- JavaScript API - Core SDK methods