Skip to main content
In-app messages let you deliver personalized content — promotional banners, onboarding prompts, important alerts — directly inside your React Native app without requiring a separate release. You create and configure messages in the Tappd dashboard, and the SDK automatically fetches, evaluates trigger conditions, and hands them off to your app to render using the built-in MessageRenderer component.

Configuration

Enable in-app messages when you initialize the SDK:
const tappd = new TappdSDK({
  appId: 'YOUR_APP_ID',
  apiUrl: 'https://sdk.gotappd.com/api/v1/sdk',
  enableInAppMessages: true,
  autoDisplayMessages: true,
  messagePollingInterval: 30,
});
enableInAppMessages
boolean
default:"true"
Enable or disable in-app message rendering in your app. Set to false if you want to fully suppress all in-app messages.
autoDisplayMessages
boolean
default:"true"
When true, the SDK automatically fetches pending messages for the identified user, evaluates trigger conditions, and calls your render callback when a message is ready to display.
messagePollingInterval
number
default:"30"
How often (in seconds) the SDK polls for new messages. Lower values give more timely delivery; higher values reduce network activity.

React Native Integration

To render messages, register a callback with the SDK so it can hand off a message object to your component tree whenever one is ready to display.

Basic Setup

import React, { useState, useEffect } from 'react';
import { View } from 'react-native';
import TappdSDK from '@tappd/mobile-sdk';
import { MessageRenderer } from '@tappd/mobile-sdk/src/renderers/MessageRenderer';

const tappd = new TappdSDK({
  appId: 'YOUR_APP_ID',
  enableInAppMessages: true,
  autoDisplayMessages: true,
});

function App() {
  const [currentMessage, setCurrentMessage] = useState(null); // InAppMessage | null

  useEffect(() => {
    // Register the callback the SDK calls when a message is ready
    tappd.setMessageRenderCallback((message) => {
      setCurrentMessage(message);
    });

    return () => {
      tappd.setMessageRenderCallback(() => {});
    };
  }, []);

  const handleDismiss = async (messageId) => {
    await tappd.dismissMessage(messageId);
    setCurrentMessage(null);
  };

  const handleButtonClick = async (messageId, link, text) => {
    console.log('Button clicked:', { messageId, link, text });
    await handleDismiss(messageId);
  };

  return (
    <View style={{ flex: 1 }}>
      {/* Your app content */}

      {currentMessage && (
        <MessageRenderer
          message={currentMessage}
          onDismiss={handleDismiss}
          onButtonClick={handleButtonClick}
        />
      )}
    </View>
  );
}

Automatic Display

When autoDisplayMessages is true, the SDK handles the full delivery pipeline for you:
1

Fetch pending messages

After identify() is called, the SDK automatically retrieves any pending messages for that user from the Tappd API.
2

Evaluate trigger conditions

Each message is evaluated against its configured trigger (immediate, timed delay, or event-based) and expiration date.
3

Invoke your render callback

When a message is ready to display, the SDK calls the function you registered with setMessageRenderCallback(), passing the message object.
4

Track interactions automatically

The SDK automatically tracks message.viewed, message.clicked, and message.dismissed events as the user interacts with the message.
5

Poll for new messages

The SDK continues polling at the interval defined by messagePollingInterval to pick up newly published messages.
const tappd = new TappdSDK({
  appId: 'YOUR_APP_ID',
  enableInAppMessages: true,
  autoDisplayMessages: true,
  messagePollingInterval: 30,
});

// Identify the user — automatic message fetching begins here
await tappd.identify({
  external_id: 'user_123',
  email: 'john@example.com',
  name: 'John Doe',
});

// Register the render callback
tappd.setMessageRenderCallback((message) => {
  setCurrentMessage(message);
});

Manual Control

If you prefer to control when messages are fetched and displayed, you can bypass automatic display and call these methods directly.
// Fetch all pending messages for the current user
const messages = await tappd.getInAppMessages();
console.log(`Found ${messages.length} pending messages`);

Message Types

Message Blocks

Messages are composed of one or more content blocks, each rendered as a native React Native component.
Renders images using React Native’s Image component.
PropertyDescription
urlImage URL
altAlt text for accessibility
widthImage width
heightImage height
alignmentleft, center, right, or full
Renders text content using React Native’s Text component.
PropertyDescription
contentText content to display
fontSizesm, base, lg, xl, 2xl, or a custom size
fontWeightnormal or bold
colorText color (hex, rgb, or named color)
alignmentleft, center, right, or justify
Renders a tappable button using React Native’s TouchableOpacity.
PropertyDescription
textButton label
linkURL opened via Linking.openURL()
textColorButton text color
backgroundColorButton background color
linkBehaviorbrowser (open URL), in_app, or deeplink
alignmentleft, center, or right
Renders arbitrary HTML content using react-native-render-html.
PropertyDescription
contentFull HTML string to render
react-native-render-html is bundled inside the Tappd Mobile SDK. You do not need to install it separately in your project.

Event Tracking

The SDK tracks all standard message interactions automatically:
EventTriggered when
message.viewedThe message is displayed to the user
message.clickedA button inside the message is tapped
message.dismissedThe user dismisses the message
For custom interactions, call trackMessageEvent() directly:
await tappd.trackMessageEvent('message_id_123', 'clicked', {
  buttonText: 'Sign Up',
  buttonLink: 'https://example.com/signup',
});

Integration with React Navigation

Place MessageRenderer at the root of your navigation tree so messages appear above all screens, regardless of which route is currently active:
import { NavigationContainer } from '@react-navigation/native';
import { MessageRenderer } from '@tappd/mobile-sdk/src/renderers/MessageRenderer';

function App() {
  const [currentMessage, setCurrentMessage] = useState(null);

  useEffect(() => {
    tappd.setMessageRenderCallback((message) => {
      setCurrentMessage(message);
    });
  }, []);

  return (
    <NavigationContainer>
      {/* Your navigation structure */}

      {currentMessage && (
        <MessageRenderer
          message={currentMessage}
          onDismiss={(id) => {
            tappd.dismissMessage(id);
            setCurrentMessage(null);
          }}
        />
      )}
    </NavigationContainer>
  );
}

Complete Example

import React, { useState, useEffect } from 'react';
import { View, StyleSheet, Linking } from 'react-native';
import TappdSDK from '@tappd/mobile-sdk';
import { MessageRenderer } from '@tappd/mobile-sdk/src/renderers/MessageRenderer';

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

export default function App() {
  const [currentMessage, setCurrentMessage] = useState(null); // InAppMessage | null

  useEffect(() => {
    tappd.setMessageRenderCallback((message) => {
      setCurrentMessage(message);
    });

    tappd.identify({
      external_id: 'user_123',
      email: 'john@example.com',
      name: 'John Doe',
    });

    return () => {
      // Clear the callback on unmount
      tappd.setMessageRenderCallback(() => {});
    };
  }, []);

  const handleDismiss = async (messageId) => {
    await tappd.dismissMessage(messageId);
    setCurrentMessage(null);
  };

  const handleButtonClick = async (messageId, link, text) => {
    if (link && link !== '#') {
      Linking.openURL(link);
    }
    await handleDismiss(messageId);
  };

  return (
    <View style={styles.container}>
      {/* Your app content */}

      {currentMessage && (
        <MessageRenderer
          message={currentMessage}
          onDismiss={handleDismiss}
          onButtonClick={handleButtonClick}
        />
      )}
    </View>
  );
}

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

API Reference

MethodReturnsDescription
setMessageRenderCallback(callback)voidRegisters the function the SDK calls when a message is ready to display.
getInAppMessages()Promise<InAppMessage[]>Fetches all pending in-app messages for the current user.
displayInAppMessage(message)Promise<void>Displays a specific message by calling the registered render callback.
displayPendingMessages()Promise<void>Fetches and displays all messages that are ready to show.
dismissMessage(messageId)Promise<void>Dismisses a message and tracks the message.dismissed event.
trackMessageEvent(messageId, eventType, metadata?)Promise<void>Manually tracks a message interaction with optional metadata.

Troubleshooting

Work through this checklist if messages aren’t appearing in your app:
  1. Confirm enableInAppMessages is set to true in your SDK config.
  2. Verify you have called setMessageRenderCallback() in your root component.
  3. Make sure identify() has been called — messages are user-specific.
  4. Enable debug: true in the SDK config and check the console for errors.
  5. Confirm the messages are published in your Tappd dashboard.
  6. Check that any configured trigger conditions are met.
If the same message keeps reappearing:
  1. Increase messagePollingInterval to poll less aggressively.
  2. Check the expiration settings on the message in the dashboard.
  3. Ensure dismissMessage() is being called correctly when a message is dismissed so the SDK records the dismissal.
If message styles look wrong or conflict with your app:
  1. Check for StyleSheet conflicts between your app styles and the MessageRenderer component.
  2. Ensure MessageRenderer is rendered at the correct level in the component tree (ideally at the root, above your navigation structure).
  3. Test on both iOS and Android — some styles, such as shadows and font rendering, differ between platforms.