Skip to content

Banners

The Tappd Mobile SDK supports displaying banners created in your Tappd dashboard. Banners are placement-based content that can be rendered in your React Native app, with support for targeting, scheduling, frequency capping, and A/B testing.

Overview

Banners are different from in-app messages:

  • Placement-based: Banners are attached to specific screens or components using placement identifiers
  • Targeting: Support for customer segments, rules, and targeting conditions
  • Scheduling: Start/end dates, active hours, and timezone support
  • Frequency Capping: Control how often banners are shown to users
  • A/B Testing: Multiple variants with traffic allocation
  • Analytics: Track displays, clicks, dismissals, and conversions

Quick Start

The SDK handles all API calls and provides banner data. You can render banners using the provided data:

javascript
import TappdSDK from '@tappd/mobile-sdk';

const tappd = new TappdSDK({
  appId: 'YOUR_APP_ID',
  apiUrl: 'https://sdk.gotappd.com/api/v1/sdk'
});

// Identify user first (required for targeting)
await tappd.identify({
  external_id: 'user_123',
  email: 'john@example.com',
  name: 'John Doe'
});

// Fetch banners for a placement
const banners = await tappd.getBanners('HomeScreen');

// Render banners in your UI
banners.forEach(({ banner, variant }) => {
  // Render your banner component
});

Banners use a placement system to attach to specific screens or components. In React Native, placements are typically screen names or component identifiers:

Placement Types

  1. Screen-based Placement: Attach banners to specific screens
  2. Component-based Placement: Attach banners to specific components
  3. Custom Placement: Use custom identifiers for placement

Example:

javascript
// Fetch banners for a specific screen
const banners = await tappd.getBanners('HomeScreen');

// Fetch banners for a component
const banners = await tappd.getBanners('header-banner');

Fetching Banners

Fetch eligible banners for a specific placement:

javascript
// Fetch banners (with caching)
const banners = await tappd.getBanners('HomeScreen');

// Force refresh (bypass cache)
const banners = await tappd.getBanners('HomeScreen', { forceRefresh: true });

The SDK automatically:

  • Fetches banners from the API
  • Caches results for 5 minutes
  • Handles API errors gracefully

The API returns an array of banner objects with the following structure:

javascript
[
  {
    banner: {
      _id: "banner_id_123",
      name: "Summer Sale Banner",
      placement: {
        type: "css_selector",
        cssSelector: "HomeScreen",
        dataAttribute: null,
        dataAttributeValue: null
      }
    },
    variant: {
      _id: "variant_id_456",
      name: "Variant A",
      content: {
        title: "Summer Sale",
        message: "Get 50% off on all items!",
        imageUrl: "https://example.com/banner.jpg",
        link: "https://example.com/sale",
        cta: {
          text: "Shop Now",
          link: "https://example.com/sale",
          action: "link"
        }
      },
      design: {
        position: "top",
        layout: "horizontal",
        backgroundColor: "#ffffff",
        textColor: "#000000",
        ctaBackgroundColor: "#007bff",
        ctaTextColor: "#ffffff",
        fontFamily: "System",
        fontSize: "16px",
        padding: "16px",
        margin: "0",
        borderRadius: "4px"
      },
      config: {
        dismissible: true,
        autoHide: false,
        autoHideDelay: 5000,
        animation: "fade",
        closeButton: true
      }
    }
  }
]

Rendering Banners in React Native

Create a reusable banner component:

javascript
import React, { useState, useEffect } from 'react';
import {
  View,
  Text,
  Image,
  TouchableOpacity,
  StyleSheet,
  Animated,
  Linking
} from 'react-native';
import TappdSDK from '@tappd/mobile-sdk';

const tappd = new TappdSDK({
  appId: 'YOUR_APP_ID',
  apiUrl: 'https://sdk.gotappd.com/api/v1/sdk'
});

// Banner Component
function Banner({ banner, variant, onDismiss }) {
  const [fadeAnim] = useState(new Animated.Value(0));
  const [displayId, setDisplayId] = useState(null);

  useEffect(() => {
    // Track display
    tappd.banners.display(banner._id, variant._id, banner.placement.cssSelector)
      .then(id => setDisplayId(id));

    // Animate in
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: 300,
      useNativeDriver: true
    }).start();

    // Auto-hide if configured
    if (variant.config.autoHide) {
      const timer = setTimeout(() => {
        handleDismiss();
      }, variant.config.autoHideDelay);
      return () => clearTimeout(timer);
    }
  }, []);

  const handleDismiss = async () => {
    if (displayId) {
      await tappd.banners.dismiss(banner._id, displayId);
    }
    onDismiss();
  };

  const handleCTAClick = async () => {
    if (displayId) {
      await tappd.banners.click(banner._id, displayId);
    }
    
    // Handle CTA action
    const cta = variant.content.cta;
    if (cta.action === 'link' && cta.link) {
      Linking.openURL(cta.link);
    } else if (cta.action === 'close') {
      handleDismiss();
    }
  };

  const design = variant.design;
  const content = variant.content;

  return (
    <Animated.View
      style={[
        styles.banner,
        {
          opacity: fadeAnim,
          backgroundColor: design.backgroundColor,
          padding: parseInt(design.padding) || 16,
          borderRadius: parseInt(design.borderRadius) || 4,
          margin: parseInt(design.margin) || 0
        }
      ]}
    >
      <View style={styles.content}>
        {/* Image */}
        {content.imageUrl && (
          <Image
            source={{ uri: content.imageUrl }}
            style={styles.image}
            resizeMode="contain"
          />
        )}

        {/* Text Content */}
        <View style={styles.textContainer}>
          {content.title && (
            <Text
              style={[
                styles.title,
                {
                  color: design.textColor,
                  fontSize: parseInt(design.fontSize) || 16
                }
              ]}
            >
              {content.title}
            </Text>
          )}
          {content.message && (
            <Text
              style={[
                styles.message,
                {
                  color: design.textColor,
                  fontSize: parseInt(design.fontSize) || 14
                }
              ]}
            >
              {content.message}
            </Text>
          )}
        </View>

        {/* CTA Button */}
        {content.cta && content.cta.text && (
          <TouchableOpacity
            style={[
              styles.ctaButton,
              {
                backgroundColor: design.ctaBackgroundColor,
                borderRadius: parseInt(design.borderRadius) || 4
              }
            ]}
            onPress={handleCTAClick}
          >
            <Text
              style={[
                styles.ctaText,
                { color: design.ctaTextColor }
              ]}
            >
              {content.cta.text}
            </Text>
          </TouchableOpacity>
        )}

        {/* Close Button */}
        {variant.config.dismissible && variant.config.closeButton && (
          <TouchableOpacity
            style={styles.closeButton}
            onPress={handleDismiss}
          >
            <Text style={styles.closeButtonText}>×</Text>
          </TouchableOpacity>
        )}
      </View>
    </Animated.View>
  );
}

const styles = StyleSheet.create({
  banner: {
    width: '100%',
    marginBottom: 8
  },
  content: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between'
  },
  image: {
    width: 60,
    height: 60,
    marginRight: 12
  },
  textContainer: {
    flex: 1,
    marginRight: 12
  },
  title: {
    fontWeight: 'bold',
    marginBottom: 4
  },
  message: {
    fontSize: 14
  },
  ctaButton: {
    paddingHorizontal: 16,
    paddingVertical: 8,
    marginLeft: 8
  },
  ctaText: {
    fontWeight: '600',
    fontSize: 14
  },
  closeButton: {
    padding: 8,
    marginLeft: 8
  },
  closeButtonText: {
    fontSize: 24,
    color: '#666'
  }
});

export default Banner;

Using Banners in Your App

Integrate banners into your screens:

javascript
import React, { useState, useEffect } from 'react';
import { View, StyleSheet, ScrollView } from 'react-native';
import TappdSDK from '@tappd/mobile-sdk';
import Banner from './Banner';

const tappd = new TappdSDK({
  appId: 'YOUR_APP_ID',
  apiUrl: 'https://sdk.gotappd.com/api/v1/sdk'
});

function HomeScreen() {
  const [banners, setBanners] = useState([]);

  useEffect(() => {
    loadBanners();
  }, []);

  const loadBanners = async () => {
    try {
      const bannerData = await tappd.getBanners('HomeScreen');
      setBanners(bannerData);
    } catch (error) {
      console.error('Failed to load banners:', error);
    }
  };

  const handleDismiss = (bannerId) => {
    setBanners(banners.filter(b => b.banner._id !== bannerId));
  };

  return (
    <ScrollView style={styles.container}>
      {/* Render banners at top */}
      {banners.map(({ banner, variant }) => (
        <Banner
          key={banner._id}
          banner={banner}
          variant={variant}
          onDismiss={() => handleDismiss(banner._id)}
        />
      ))}
      
      {/* Your screen content */}
      <View style={styles.content}>
        {/* ... */}
      </View>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  content: {
    padding: 16
  }
});

export default HomeScreen;

Manual Event Tracking

For custom implementations, you can manually track banner events:

Track Display

javascript
// Track banner display and get displayId
const displayId = await tappd.banners.display(
  bannerId,      // Banner ID
  variantId,     // Variant ID
  selector       // Placement selector (e.g., 'HomeScreen')
);

Track Click

javascript
// Track banner click
await tappd.banners.click(bannerId, displayId);

Track Dismiss

javascript
// Track banner dismiss
await tappd.banners.dismiss(bannerId, displayId);

Handle different banner positions:

javascript
function Banner({ banner, variant, ...props }) {
  const design = variant.design;
  const position = design.position || 'top';

  const positionStyles = {
    top: { position: 'absolute', top: 0, left: 0, right: 0, zIndex: 1000 },
    bottom: { position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 1000 },
    center: { alignItems: 'center', justifyContent: 'center' }
  };

  return (
    <View style={[styles.banner, positionStyles[position]]}>
      {/* Banner content */}
    </View>
  );
}

Complete Example with React Navigation

Integrate banners with React Navigation:

javascript
import React, { useState, useEffect } from 'react';
import { View, StyleSheet } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import TappdSDK from '@tappd/mobile-sdk';
import Banner from './components/Banner';

const tappd = new TappdSDK({
  appId: 'YOUR_APP_ID',
  apiUrl: 'https://sdk.gotappd.com/api/v1/sdk'
});

const Stack = createStackNavigator();

function App() {
  const [banners, setBanners] = useState([]);
  const [currentScreen, setCurrentScreen] = useState('Home');

  useEffect(() => {
    // Identify user
    tappd.identify({
      external_id: 'user_123',
      email: 'john@example.com',
      name: 'John Doe'
    });

    // Load banners for current screen
    loadBanners(currentScreen);
  }, [currentScreen]);

  const loadBanners = async (screenName) => {
    try {
      const bannerData = await tappd.getBanners(screenName);
      setBanners(bannerData);
    } catch (error) {
      console.error('Failed to load banners:', error);
    }
  };

  const handleDismiss = (bannerId) => {
    setBanners(banners.filter(b => b.banner._id !== bannerId));
  };

  return (
    <NavigationContainer>
      <View style={styles.container}>
        <Stack.Navigator>
          <Stack.Screen
            name="Home"
            component={HomeScreen}
            listeners={{
              focus: () => setCurrentScreen('Home')
            }}
          />
          <Stack.Screen
            name="Profile"
            component={ProfileScreen}
            listeners={{
              focus: () => setCurrentScreen('Profile')
            }}
          />
        </Stack.Navigator>

        {/* Render banners */}
        {banners.map(({ banner, variant }) => (
          <Banner
            key={banner._id}
            banner={banner}
            variant={variant}
            onDismiss={() => handleDismiss(banner._id)}
          />
        ))}
      </View>
    </NavigationContainer>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1
  }
});

export default App;

Targeting

Banners support customer targeting:

  • Segments: Target specific customer segments
  • Rules: Custom targeting rules based on customer attributes
  • Enabled/Disabled: Toggle targeting on/off

Banners with targeting enabled will only be returned if the customer matches the targeting criteria.

Scheduling

Banners support scheduling:

  • Start/End Dates: Set when banners should be active
  • Active Hours: Define specific hours when banners should display
  • Active Days: Define specific days of the week
  • Timezone: Set timezone for schedule calculations

Frequency Capping

Control how often banners are shown:

  • Max Displays: Maximum number of times to show a banner
  • Period: Time period (session, hour, day, week, month)
  • Enabled/Disabled: Toggle frequency capping

A/B Testing

Banners support A/B testing with variants:

  • Multiple Variants: Create multiple banner variants
  • Traffic Allocation: Control percentage of traffic for each variant
  • Experiment Assignment: Automatic variant assignment based on customer ID

Analytics

Banners track comprehensive analytics:

  • Views: Number of times banner was displayed
  • Clicks: Number of times banner was clicked
  • Dismissals: Number of times banner was dismissed
  • Conversions: Number of conversions from banner
  • CTR: Click-through rate (clicks / views)

Best Practices

  1. Identify Users Early: Call identify() before fetching banners to enable targeting
  2. Screen-based Placement: Use screen names as placement identifiers for easy management
  3. Track All Interactions: Use banners.display(), banners.click(), and banners.dismiss() for accurate analytics
  4. Handle Navigation: Reload banners when navigating between screens
  5. Error Handling: Handle API errors gracefully
  6. Performance: SDK caches banners for 5 minutes to reduce API calls
  7. Responsive Design: Ensure banners work on different screen sizes
  8. Animation: Use React Native Animated API for smooth animations

Troubleshooting

Banners not displaying

  1. Check that user is identified (identify() has been called)
  2. Verify placement identifier matches screen/component name
  3. Check console for errors
  4. Verify banner is active and published in dashboard
  5. Check targeting conditions are met
  6. Verify schedule is active
  7. Check frequency capping hasn't been reached

Banners displaying incorrectly

  1. Check banner styles are applied correctly
  2. Verify container dimensions
  3. Check for style conflicts
  4. Ensure proper positioning styles

Tracking not working

  1. Verify API endpoints are correct (check apiUrl config)
  2. Check that displayId is stored and passed correctly
  3. Check console for network errors
  4. Verify App ID is correct

API Reference

getBanners(selector, options?)

Fetch banners for a placement.

Parameters:

  • selector (string): Placement identifier (screen name, component name, etc.)
  • options (object, optional):
    • forceRefresh (boolean): Force refresh and bypass cache (default: false)

Returns: Promise<Banner[]> - Array of banner data

banners.display(bannerId, variantId, selector)

Manually track banner display.

Parameters:

  • bannerId (string): Banner ID
  • variantId (string): Variant ID
  • selector (string): Placement selector

Returns: Promise<string> - Display ID for use with click/dismiss

banners.click(bannerId, displayId)

Manually track banner click.

Parameters:

  • bannerId (string): Banner ID
  • displayId (string): Display ID from banners.display()

Returns: Promise<void>

banners.dismiss(bannerId, displayId)

Manually track banner dismiss.

Parameters:

  • bannerId (string): Banner ID
  • displayId (string): Display ID from banners.display()

Returns: Promise<void>

Released under the MIT License.