Skip to main content

Overview

MagicFeedback Popup SDK uses a renderer architecture that allows you to customize how popups are displayed. By default, the SDK provides browser-based rendering, but you can create custom renderers for specialized environments.

PopupRenderer Interface

All renderers must implement the PopupRenderer interface:
src/platform/renderer.ts
export interface PopupRenderer {
  /** Prepare resources if applicable */
  init?(): void;
  
  /** Show popup */
  show(
    surveyId: string,
    productId: string,
    actions: PopupActions | undefined,
    emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void,
    onClose: () => void,
    env?: string
  ): void;
  
  /** Hide popup */
  hide(): void;
}

Interface Methods

init
() => void
Optional initialization method called when the SDK is initialized. Use this to prepare resources, create DOM elements, or set up native modules.
show
function
required
Displays the popup with the specified configuration.Parameters:
  • surveyId: Unique identifier for the survey
  • productId: Product identifier
  • actions: Custom action buttons configuration
  • emit: Event emitter function for tracking user interactions
  • onClose: Callback to invoke when popup is closed
  • env: Environment setting (‘production’ or ‘development’)
hide
() => void
required
Hides the currently displayed popup and cleans up resources.

Built-in Renderers

The SDK includes three built-in renderers:

BrowserPopupRenderer

Default renderer for web browsers. Creates a fixed-position overlay container and renders the popup UI.
src/platform/renderer.ts
export class BrowserPopupRenderer implements PopupRenderer {
  private container: HTMLElement | null = null;

  init(): void {
    if (typeof document === 'undefined') return;
    if (!this.container) {
      this.container = document.createElement('div');
      this.container.id = 'deepdots-popup-container';
      this.container.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        display: none;
        z-index: 999999;
        background: rgba(0, 0, 0, 0.5);
        justify-content: center;
        align-items: center;
      `;
      document.body.appendChild(this.container);
    }
  }

  show(
    surveyId: string,
    productId: string,
    actions: PopupActions | undefined,
    emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void,
    onClose: () => void,
    env: string = 'production'
  ): void {
    if (!this.container) this.init();
    if (!this.container) return;
    renderPopup(this.container, surveyId, productId, actions, emit, onClose, env);
  }

  hide(): void {
    if (this.container) {
      this.container.style.display = 'none';
      this.container.innerHTML = '';
    }
  }
}

NoopPopupRenderer

A no-operation renderer for server-side rendering or environments without DOM support.
src/platform/renderer.ts
export class NoopPopupRenderer implements PopupRenderer {
  show(): void { /* no-op */ }
  hide(): void { /* no-op */ }
}

ReactNativePopupRenderer

Specialized renderer for React Native applications. See React Native Support for details.

Creating a Custom Renderer

1

Implement the Interface

Create a class that implements the PopupRenderer interface:
import { PopupRenderer, DeepdotsEventType, PopupActions } from '@magicfeedback/popup-sdk';

export class MyCustomRenderer implements PopupRenderer {
  private isVisible = false;

  init(): void {
    // Initialize your custom rendering system
    console.log('Custom renderer 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.isVisible = true;
    
    // Emit popup shown event
    emit('popup_shown', surveyId);
    
    // Your custom rendering logic here
    console.log('Showing popup:', { surveyId, productId, env });
    
    // Example: Show popup and handle user interaction
    this.renderCustomUI(surveyId, productId, actions, (data) => {
      // User completed the survey
      emit('survey_completed', surveyId, data);
      onClose();
    });
  }

  hide(): void {
    this.isVisible = false;
    // Clean up your custom UI
    console.log('Hiding popup');
  }

  private renderCustomUI(
    surveyId: string,
    productId: string,
    actions: PopupActions | undefined,
    onComplete: (data: Record<string, unknown>) => void
  ): void {
    // Implement your custom UI rendering logic
    // This could integrate with a modal library, native UI, etc.
  }
}
2

Register the Renderer

Use the setRenderer() method to register your custom renderer:
src/core/deepdots-popups.ts
import { DeepdotsPopups } from '@magicfeedback/popup-sdk';
import { MyCustomRenderer } from './my-custom-renderer';

const popups = new DeepdotsPopups();

// Set custom renderer before initializing
popups.setRenderer(new MyCustomRenderer());

// Initialize the SDK
popups.init({
  apiKey: 'your-api-key',
  mode: 'client',
  popups: [/* your popup definitions */]
});
The setRenderer() method automatically calls init() on your renderer if the SDK is already initialized.
3

Handle Events

Your renderer should emit appropriate events during the popup lifecycle:
// When popup is shown
emit('popup_shown', surveyId, { popupId: 'popup-id' });

// When user interacts
emit('popup_clicked', surveyId, { action: 'button-clicked' });

// When survey is completed
emit('survey_completed', surveyId, { 
  rating: 5, 
  feedback: 'Great product!' 
});

Use Cases

Custom renderers are useful for:
  • Mobile Apps: Integrate with React Native, Flutter, or native mobile UI (see React Native Support)
  • Desktop Applications: Electron or Tauri apps with custom window management
  • Game Engines: Unity, Unreal Engine, or web-based game frameworks
  • Custom UI Libraries: Integration with specific component libraries or design systems
  • Testing: Mock renderers for automated testing

Example: Modal Library Integration

Here’s an example integrating with a popular modal library:
import { PopupRenderer, DeepdotsEventType, PopupActions } from '@magicfeedback/popup-sdk';
import { openModal, closeModal } from 'your-modal-library';

export class ModalLibraryRenderer implements PopupRenderer {
  private currentModalId: string | null = null;

  show(
    surveyId: string,
    productId: string,
    actions: PopupActions | undefined,
    emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void,
    onClose: () => void,
    env?: string
  ): void {
    this.currentModalId = openModal({
      title: 'We value your feedback',
      content: this.buildSurveyContent(surveyId, productId),
      onSubmit: (data) => {
        emit('survey_completed', surveyId, data);
        this.hide();
        onClose();
      },
      onCancel: () => {
        this.hide();
        onClose();
      }
    });

    emit('popup_shown', surveyId);
  }

  hide(): void {
    if (this.currentModalId) {
      closeModal(this.currentModalId);
      this.currentModalId = null;
    }
  }

  private buildSurveyContent(surveyId: string, productId: string): HTMLElement {
    // Build your survey UI
    const container = document.createElement('div');
    // ... add survey elements
    return container;
  }
}
Always call the onClose callback when your popup is dismissed to ensure proper cleanup and state management.

API Reference

setRenderer()

Sets a custom renderer for the SDK.
src/core/deepdots-popups.ts
public setRenderer(renderer: PopupRenderer): void {
  this.renderer = renderer;
  if (this.initialized && this.renderer.init) {
    this.renderer.init();
  }
}
Parameters:
  • renderer: An instance implementing the PopupRenderer interface
Example:
const popups = new DeepdotsPopups();
popups.setRenderer(new MyCustomRenderer());

Next Steps

Build docs developers (and LLMs) love