Skip to main content

Overview

The MagicFeedback Popup SDK includes built-in support for React Native through a specialized renderer that adapts to the React Native environment. The SDK automatically detects React Native and uses the appropriate renderer.

ReactNativePopupRenderer

The ReactNativePopupRenderer is a specialized implementation designed for React Native apps. It provides a bridge between the SDK and your React Native UI layer.
src/platform/react-native-renderer.ts
export class ReactNativePopupRenderer implements PopupRenderer {
  private mounted = false;
  private lastSurveyId: string | null = null;
  private emitFn: ((type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void) | null = null;
  private onCloseFn: (() => void) | null = null;
  private completed = false;

  init(): void {
    // Prepare any state / native module listeners
    // This is where you could initialize a NativeModule or global store
  }

  show(
    surveyId: string,
    productId: string,
    data: Record<string, unknown> | undefined,
    emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void,
    onClose: () => void
  ): void {
    this.mounted = true;
    this.lastSurveyId = surveyId;
    this.emitFn = emit;
    this.onCloseFn = onClose;
    this.completed = false;
    
    // Emit popup shown event
    queueMicrotask(() => emit('popup_clicked', surveyId, data));
    
    console.log('[ReactNativePopupRenderer] show survey:', { surveyId, productId, data });
  }

  /** Complete the survey from the native layer */
  public completeSurvey(data?: Record<string, unknown>): void {
    if (!this.mounted || this.completed || !this.lastSurveyId || !this.emitFn) return;
    
    this.completed = true;
    this.emitFn('survey_completed', this.lastSurveyId, data);
    
    // Close popup
    this.onCloseFn?.();
    this.mounted = false;
    
    console.log('[ReactNativePopupRenderer] survey completed:', { surveyId: this.lastSurveyId, data });
    this.lastSurveyId = null;
  }

  hide(): void {
    if (!this.mounted) return;
    this.mounted = false;
    console.log('[ReactNativePopupRenderer] hide survey:', this.lastSurveyId);
    this.lastSurveyId = null;
  }
}

Automatic Detection

The SDK automatically detects React Native environments:
src/platform/react-native-renderer.ts
export function isReactNativeEnv(): boolean {
  return typeof navigator !== 'undefined' && (navigator as any).product === 'ReactNative';
}

export function createReactNativeRenderer(): PopupRenderer {
  return new ReactNativePopupRenderer();
}
The SDK checks navigator.product === 'ReactNative' to determine if it’s running in a React Native environment.

Setup Guide

1

Install the SDK

Install the MagicFeedback Popup SDK in your React Native project:
npm install @magicfeedback/popup-sdk
# or
yarn add @magicfeedback/popup-sdk
2

Initialize the SDK

Initialize the SDK in your React Native app. The SDK will automatically use the React Native renderer:
App.tsx
import React, { useEffect } from 'react';
import { DeepdotsPopups } from '@magicfeedback/popup-sdk';

const popups = new DeepdotsPopups();

export default function App() {
  useEffect(() => {
    popups.init({
      apiKey: 'your-api-key',
      mode: 'client',
      debug: true,
      popups: [{
        id: 'welcome-popup',
        surveyId: 'survey-123',
        productId: 'product-456',
        title: 'Welcome!',
        message: 'How do you like our app?',
        triggers: {
          type: 'time_on_page',
          value: 10
        }
      }]
    });

    popups.autoLaunch();
  }, []);

  return (
    <View style={styles.container}>
      <Text>Your App Content</Text>
    </View>
  );
}
3

Create a Modal Component

Create a React Native Modal component to display surveys:
SurveyModal.tsx
import React, { useState, useEffect } from 'react';
import { Modal, View, Text, Button, StyleSheet } from 'react-native';
import { DeepdotsPopups } from '@magicfeedback/popup-sdk';

interface SurveyModalProps {
  popups: DeepdotsPopups;
}

export const SurveyModal: React.FC<SurveyModalProps> = ({ popups }) => {
  const [visible, setVisible] = useState(false);
  const [surveyData, setSurveyData] = useState<any>(null);

  useEffect(() => {
    // Listen for popup shown events
    const handlePopupShown = (event: any) => {
      setVisible(true);
      setSurveyData(event);
    };

    popups.on('popup_shown', handlePopupShown);

    return () => {
      popups.off('popup_shown', handlePopupShown);
    };
  }, [popups]);

  const handleSubmit = (rating: number) => {
    const renderer = (popups as any).renderer;
    if (renderer && typeof renderer.completeSurvey === 'function') {
      renderer.completeSurvey({ rating });
    }
    setVisible(false);
  };

  const handleClose = () => {
    setVisible(false);
  };

  return (
    <Modal
      visible={visible}
      transparent
      animationType="fade"
      onRequestClose={handleClose}
    >
      <View style={styles.overlay}>
        <View style={styles.modal}>
          <Text style={styles.title}>How do you like our app?</Text>
          <View style={styles.buttons}>
            {[1, 2, 3, 4, 5].map((rating) => (
              <Button
                key={rating}
                title={String(rating)}
                onPress={() => handleSubmit(rating)}
              />
            ))}
          </View>
          <Button title="Close" onPress={handleClose} />
        </View>
      </View>
    </Modal>
  );
};

const styles = StyleSheet.create({
  overlay: {
    flex: 1,
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
    justifyContent: 'center',
    alignItems: 'center',
  },
  modal: {
    backgroundColor: 'white',
    borderRadius: 10,
    padding: 20,
    width: '80%',
  },
  title: {
    fontSize: 18,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  buttons: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    marginBottom: 20,
  },
});
4

Add Modal to Your App

Include the survey modal in your app component:
App.tsx
import React, { useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { DeepdotsPopups } from '@magicfeedback/popup-sdk';
import { SurveyModal } from './SurveyModal';

const popups = new DeepdotsPopups();

export default function App() {
  useEffect(() => {
    popups.init({
      apiKey: 'your-api-key',
      mode: 'client',
      debug: true,
      popups: [/* your popup definitions */]
    });

    popups.autoLaunch();
  }, []);

  return (
    <View style={styles.container}>
      <Text>Your App Content</Text>
      <SurveyModal popups={popups} />
    </View>
  );
}

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

Manual Triggering

You can also trigger popups manually in React Native:
import React from 'react';
import { Button } from 'react-native';
import { DeepdotsPopups } from '@magicfeedback/popup-sdk';

interface FeedbackButtonProps {
  popups: DeepdotsPopups;
}

export const FeedbackButton: React.FC<FeedbackButtonProps> = ({ popups }) => {
  const handlePress = () => {
    popups.showByPopupId('feedback-popup');
  };

  return <Button title="Give Feedback" onPress={handlePress} />;
};

Custom React Native Renderer

You can create a custom renderer for more advanced React Native integrations:
CustomReactNativeRenderer.ts
import { PopupRenderer, DeepdotsEventType, PopupActions } from '@magicfeedback/popup-sdk';
import { EventEmitter } from 'events';

const eventBus = new EventEmitter();

export class CustomReactNativeRenderer implements PopupRenderer {
  private mounted = false;
  private currentSurveyId: string | null = null;
  private emitFn: ((type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void) | null = null;
  private onCloseFn: (() => void) | null = null;

  init(): void {
    // Set up event listeners for native bridge
    console.log('[CustomReactNativeRenderer] Initialized');
  }

  show(
    surveyId: string,
    productId: string,
    actions: PopupActions | undefined,
    emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void,
    onClose: () => void,
    env?: string
  ): void {
    this.mounted = true;
    this.currentSurveyId = surveyId;
    this.emitFn = emit;
    this.onCloseFn = onClose;

    // Emit event to show modal in React Native
    eventBus.emit('showSurvey', {
      surveyId,
      productId,
      actions,
      env
    });

    emit('popup_shown', surveyId);
  }

  hide(): void {
    if (!this.mounted) return;
    
    eventBus.emit('hideSurvey');
    this.mounted = false;
    this.currentSurveyId = null;
  }

  // Method to be called from React Native UI
  public submitSurvey(data: Record<string, unknown>): void {
    if (!this.currentSurveyId || !this.emitFn) return;
    
    this.emitFn('survey_completed', this.currentSurveyId, data);
    this.onCloseFn?.();
    this.hide();
  }
}

// Export event bus for React components to listen
export { eventBus };

Event Handling

Listen to SDK events in your React Native components:
usePopupEvents.ts
import { useEffect } from 'react';
import { DeepdotsPopups, DeepdotsEvent } from '@magicfeedback/popup-sdk';

export const usePopupEvents = (popups: DeepdotsPopups) => {
  useEffect(() => {
    const handlePopupShown = (event: DeepdotsEvent) => {
      console.log('Popup shown:', event);
    };

    const handlePopupClicked = (event: DeepdotsEvent) => {
      console.log('Popup clicked:', event);
    };

    const handleSurveyCompleted = (event: DeepdotsEvent) => {
      console.log('Survey completed:', event);
    };

    popups.on('popup_shown', handlePopupShown);
    popups.on('popup_clicked', handlePopupClicked);
    popups.on('survey_completed', handleSurveyCompleted);

    return () => {
      popups.off('popup_shown', handlePopupShown);
      popups.off('popup_clicked', handlePopupClicked);
      popups.off('survey_completed', handleSurveyCompleted);
    };
  }, [popups]);
};

Best Practices

The built-in ReactNativePopupRenderer is a stub implementation. You’ll need to connect it to your React Native UI layer using events, context, or a state management solution.
  • Use Modal Components: React Native’s Modal component is ideal for displaying surveys
  • Handle Lifecycle: Clean up event listeners when components unmount
  • Test on Both Platforms: Ensure your implementation works on iOS and Android
  • Consider Navigation: Handle popups gracefully when users navigate between screens
  • Respect Platform Guidelines: Follow iOS and Android UI/UX guidelines for modals

Troubleshooting

Renderer Not Detected

If the SDK doesn’t automatically detect React Native:
import { DeepdotsPopups } from '@magicfeedback/popup-sdk';
import { ReactNativePopupRenderer } from '@magicfeedback/popup-sdk/platform';

const popups = new DeepdotsPopups();
popups.setRenderer(new ReactNativePopupRenderer());

Popups Not Showing

Ensure you:
  1. Called init() before showing popups
  2. Set up event listeners for popup_shown
  3. Created a Modal component to display the UI
  4. Connected the renderer to your UI layer

Next Steps

Build docs developers (and LLMs) love