Skip to main content
Tappd banners are placement-based content units you create in the dashboard and fetch in your React Native app using a placement identifier — typically a screen name or component ID. Unlike in-app messages, which are trigger-driven and shown once, banners are designed for persistent placements and support advanced delivery controls like audience targeting, scheduling windows, frequency capping, and multi-variant A/B testing.

Quick Start

Identify your user, fetch banners for a placement, and render each result with your own component:
import TappdSDK from '@tappd/mobile-sdk';

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

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

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

banners.forEach(({ banner, variant }) => {
  // Render your banner component with the returned data
});

Placement System

Banners attach to your UI via a placement identifier — a string you define in the dashboard and pass to getBanners(). Use whatever naming convention makes sense for your app:
// Screen-based placement — use screen names
const banners = await tappd.getBanners('HomeScreen');

// Component-based placement — use component identifiers
const banners = await tappd.getBanners('header-banner');
Use screen names as placement identifiers whenever possible. Mapping banners to screens keeps your placements predictable and makes it easy to reload banners when a user navigates to a new screen.

Fetching Banners

getBanners() automatically caches results for 5 minutes to reduce API calls. Use the optional second argument to control caching and rendering behaviour:
// Fetch with caching (default — 5-minute cache)
const banners = await tappd.getBanners('HomeScreen');

// Force a fresh fetch, bypassing the cache
const banners = await tappd.getBanners('HomeScreen', { forceRefresh: true });

// Auto-render eligible banners immediately after fetching
const banners = await tappd.getBanners('HomeScreen', { autoRender: true });
OptionTypeDefaultDescription
autoRenderbooleanfalseWhen true, the SDK automatically renders eligible banners for the placement immediately after fetching, without requiring you to call banners.display() separately.
forceRefreshbooleanfalseBypass the 5-minute result cache and request fresh data from the API.
Each item in the response array contains a banner object and the winning variant object selected for the current user:
[
  {
    "banner": {
      "_id": "banner_id_123",
      "name": "Summer Sale Banner",
      "placement": {
        "type": "css_selector",
        "cssSelector": "HomeScreen"
      }
    },
    "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

Build a reusable Banner component that reads from the banner and variant objects returned by the API:
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',
});

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

  useEffect(() => {
    // Track the display and store the displayId for click/dismiss tracking
    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);
    }

    const cta = variant.content.cta;
    if (cta.action === 'link' && cta.link) {
      Linking.openURL(cta.link);
    } else if (cta.action === 'close') {
      handleDismiss();
    }
  };

  const { design, content } = variant;

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

        <View style={styles.textContainer}>
          {content.title && (
            <Text style={[styles.title, { color: design.textColor }]}>
              {content.title}
            </Text>
          )}
          {content.message && (
            <Text style={[styles.message, { color: design.textColor }]}>
              {content.message}
            </Text>
          )}
        </View>

        {content.cta?.text && (
          <TouchableOpacity
            style={[
              styles.ctaButton,
              { backgroundColor: design.ctaBackgroundColor },
            ]}
            onPress={handleCTAClick}
          >
            <Text style={[styles.ctaText, { color: design.ctaTextColor }]}>
              {content.cta.text}
            </Text>
          </TouchableOpacity>
        )}

        {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;

Manual Event Tracking

Call the banners tracking methods explicitly when you need fine-grained control over when events are recorded:
1

Track display

Call banners.display() when the banner becomes visible. Store the returned displayId — you need it for click and dismiss tracking.
const displayId = await tappd.banners.display(
  bannerId,   // Banner ID
  variantId,  // Variant ID
  selector    // Placement selector (e.g., 'HomeScreen')
);
2

Track clicks

Call banners.click() when the user taps the CTA button.
await tappd.banners.click(bannerId, displayId);
3

Track dismissals

Call banners.dismiss() when the user closes the banner.
await tappd.banners.dismiss(bannerId, displayId);
Use the position value from variant.design to apply the correct layout style:
function Banner({ banner, variant, ...props }) {
  const position = variant.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>
  );
}

Features

Audience Targeting

Show banners only to specific customer segments or users matching custom attribute rules. Banners with targeting enabled are only returned when the identified user meets the criteria.

Scheduling

Set start and end dates, restrict banners to specific hours of the day or days of the week, and define a timezone for accurate schedule calculations.

Frequency Capping

Limit how many times a banner is shown to an individual user. Configure a maximum display count and a reset period — session, hour, day, week, or month.

A/B Testing

Run experiments with multiple variants. Set traffic allocation percentages for each variant and let the SDK automatically assign users based on their customer ID.

Analytics

Every banner interaction is tracked automatically when you call the banners.* methods:
MetricDescription
ViewsTimes the banner was displayed
ClicksTimes the CTA was tapped
DismissalsTimes the banner was closed
ConversionsDownstream conversions attributed to the banner
CTRClick-through rate (clicks ÷ views)

API Reference

getBanners(selector, options?)
Promise<Banner[]>
Fetch eligible banners for a placement.
  • selector (string, required) — Placement identifier (screen name, component name, etc.)
  • options.autoRender (boolean, default false) — Automatically render eligible banners after fetching, without a separate banners.display() call.
  • options.forceRefresh (boolean, default false) — Bypass the 5-minute cache and fetch fresh data.
banners.display(bannerId, variantId, selector)
Promise<string>
Track a banner display. Returns a displayId string you must store and pass to banners.click() and banners.dismiss().
banners.click(bannerId, displayId)
Promise<void>
Track a banner CTA click. Pass the displayId returned by banners.display().
banners.dismiss(bannerId, displayId)
Promise<void>
Track a banner dismissal. Pass the displayId returned by banners.display().

Best Practices

Keep these tips in mind for reliable banner delivery and accurate analytics:
  • Identify users early — Call identify() before fetching banners so targeting rules can be evaluated.
  • Reload on navigation — Fetch banners again whenever the user navigates to a new screen so placement-specific content stays fresh.
  • Always track all three events — Display, click, and dismiss events together give you accurate CTR and frequency capping data.
  • Lean on caching — The SDK’s 5-minute cache reduces API overhead. Use forceRefresh only when you know the content has changed.
  • Handle errors gracefully — Wrap getBanners() calls in try-catch blocks so a failed fetch doesn’t break your screen.

Troubleshooting

If getBanners() returns an empty array or banners don’t appear in your UI:
  1. Make sure identify() was called before getBanners().
  2. Confirm the placement identifier exactly matches what’s configured in the dashboard.
  3. Check the browser console or device logs for network errors.
  4. Verify the banner is published and active in the Tappd dashboard.
  5. Review targeting conditions — the identified user must match any configured segment or rule.
  6. Check that the banner’s schedule window is currently active.
  7. Confirm frequency capping hasn’t been reached for this user.
If banners appear but look wrong:
  1. Verify you’re reading style values from variant.design and applying them correctly to your component.
  2. Check that the container has explicit dimensions — React Native won’t size a component with no content or constraints.
  3. Look for style conflicts between your app’s global styles and the banner component.
  4. Ensure positionStyles are applied for absolute-positioned banners (top/bottom).
If clicks and dismissals aren’t appearing in Tappd analytics:
  1. Confirm apiUrl in your SDK config points to https://sdk.gotappd.com/api/v1/sdk.
  2. Make sure you’re storing the displayId returned by banners.display() and passing it to banners.click() and banners.dismiss().
  3. Check device logs for network errors on the tracking API calls.
  4. Verify your appId is correct.