npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mutalabs/react-native-muta

v1.2.1

Published

A React Native SDK for creating dynamic, remotely configurable onboarding experiences

Downloads

171

Readme

A React Native SDK for creating dynamic, remotely configurable onboarding experiences in your mobile app. Muta allows you to create, update, and A/B test your onboarding flows without deploying app updates or writing a single line of code.

Features

  • 🎨 No-code editor - design and update flows with a drag-and-drop interface
  • 🚀 Remote updates - modify onboarding flows instantly without app releases
  • ✨ Rich components - buttons, text, images, shapes, icons, and more
  • 🔄 Smooth transitions - fade and slide animations
  • 📊 Analytics integration - track user behavior with any analytics provider
  • 📝 User input collection - gather text and multiple choice responses
  • 💪 Full TypeScript support
  • 🪶 Extremely lightweight

Installation

# Install the package and its required dependencies
npm install @mutalabs/react-native-muta react-native-webview
# or
yarn add @mutalabs/react-native-muta react-native-webview

# For React Native CLI projects only (not needed for Expo)
cd ios && pod install

Note: If you're using Expo:

  • The pod install step is not required
  • You cannot use Expo Go - you must create a fresh development build
  • Follow the official Expo guide to create a development build: Creating Development Builds

Quick Start

  1. Add the MutaRoot component to your app:
import { MutaRoot, Muta } from '@mutalabs/react-native-muta';

function App() {
  return (
    <>
      <MutaRoot apiKey="your-api-key" />
      {/* Your app content */}
    </>
  );
}
  1. Display a placement:
// Basic usage
Muta.displayPlacement({
  placementId: 'your-placement-id',
  bgColor: '#000000' // Should match your first placement screen's background color
})

// With custom loading screen and presentation type
Muta.displayPlacement({
  placementId: 'your-placement-id',
  bgColor: '#000000', // Should match your first placement screen's background color
  loadingScreen: <YourCustomLoadingScreen />,
  presentationType: 'fade' // 'slide' | 'fade' | 'none'
})

API Reference

MutaRoot

The root component that initializes the SDK and manages placements.

<MutaRoot apiKey={string} />

Muta.displayPlacement()

Displays a placement in a modal overlay.

interface DisplayPlacementOptions {
  placementId: string
  loadingScreen?: React.ReactNode
  bgColor: string // Should match your first placement screen's background color
  presentationType?: 'slide' | 'fade' | 'none'
  injectedScreens?: Array<{
    screenName: string
    component: React.ComponentType<{
      onContinue: () => void
      onBack: () => void
      variables: any[]
      screenId: string
      screenIndex: number
    }>
  }>
}

Muta.displayPlacement(options: DisplayPlacementOptions)

Options

  • placementId (required): The ID of the placement to display
  • bgColor (required): Background color that matches your first placement screen. This creates a seamless transition between your app and the placement.
  • loadingScreen (optional): A custom React component to show while the placement is loading
  • presentationType (optional): Animation style when showing the placement. Defaults to 'slide'.
    • 'slide': Slides up from the bottom
    • 'fade': Fades in from transparent
    • 'none': No animation
  • injectedScreens (optional): Array of native React components to inject into code screens (see Code Screens section below)

Example App

import { Muta, MutaRoot } from '@mutalabs/react-native-muta';

function App() {
  useEffect(() => {
    // Show placement when needed
    Muta.displayPlacement({
      placementId: 'your-placement-id',
      bgColor: '#1a1a1a', // Should match your first placement screen's background color
      presentationType: 'fade' // Optional: customize the animation
    });
  }, []);

  return (
    <>
      <MutaRoot apiKey="your-api-key" />
      {/* Your app content */}
    </>
  );
}

Custom Events

Muta allows you to emit custom events from your flows using the platform's behavior system. You can configure custom events in the Muta web editor and listen for them in your app to trigger specific actions.

Setting Up Custom Events

  1. In the Muta web editor, add an "Emit Event" behavior to any element
  2. Give your event a unique name (e.g., signup_started, premium_selected, survey_completed)
  3. Optionally add custom data to pass along with the event

Listening for Custom Events

import { Muta } from '@mutalabs/react-native-muta';

function Onboarding() {
  useEffect(() => {
    // Listen for your custom events
    const signupListener = Muta.on('signup_started', (event) => {
      console.log('User started signup process');
      // Trigger your signup flow
      navigateToSignup();
    });

    const premiumListener = Muta.on('premium_selected', (event) => {
      console.log('User selected premium option', event.eventData);
      // Handle premium selection
      setPremiumUser(true);
    });

    const surveyListener = Muta.on('survey_completed', (event) => {
      console.log('Survey answers:', event.eventData);
      // Save survey responses
      saveSurveyData(event.eventData);
    });

    // Show the placement
    Muta.displayPlacement({
      placementId: 'your-placement-id',
      bgColor: '#000000'
    });

    return () => {
      signupListener.remove();
      premiumListener.remove();
      surveyListener.remove();
    };
  }, []);

  return <View style={{ flex: 1 }} />;
}

Custom Event Structure

{
  type: string,         // Your custom event name
  timestamp: number,    // Unix timestamp in ms
  placementId: string,  // ID of the placement
  flowName?: string,    // Name of the flow
  screenIndex?: number, // Current screen index
  eventData?: any       // Custom data you configured in the editor
}

Common Use Cases

  1. User Actions: Track when users click specific CTAs or make choices
  2. Navigation Control: Trigger app navigation based on flow interactions
  3. Data Collection: Gather form submissions or survey responses
  4. Feature Flags: Enable/disable features based on onboarding choices
  5. Conversion Tracking: Track specific conversion events for analytics

Code Screens

Code Screens allow you to inject native React components into your Muta flows. This enables you to combine remotely configured flows with custom native functionality like authentication forms, payment screens, or any complex native UI.

How It Works

  1. In the Muta web editor, add a "Code Screen" to your flow and give it a unique name (e.g., "Login Screen", "Payment Form")
  2. Configure the navigation destinations for continue and back actions
  3. In your app, pass the corresponding React component when displaying the placement

Example Usage

import { Muta } from '@mutalabs/react-native-muta';
import { LoginScreen } from './screens/LoginScreen';
import { PaymentScreen } from './screens/PaymentScreen';

// Display placement with injected native screens
Muta.displayPlacement({
  placementId: 'onboarding',
  bgColor: '#000000',
  presentationType: 'none',
  injectedScreens: [
    {
      screenName: 'Login Screen',  // Must match the name in Muta editor
      component: LoginScreen,
    },
    {
      screenName: 'Payment Form',   // Must match the name in Muta editor
      component: PaymentScreen,
    },
  ],
});

Creating a Code Screen Component

Your component will receive props for navigation and flow data:

interface CodeScreenProps {
  onContinue: () => void      // Navigate to configured continue destination
  onBack: () => void          // Navigate to configured back destination
  variables: any[]            // Current flow variables
  screenId: string            // Unique screen ID
  screenIndex: number         // Screen position in flow
}

function LoginScreen({ onContinue, onBack, variables }: CodeScreenProps) {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleLogin = async () => {
    try {
      await loginUser(email, password);
      onContinue(); // Navigate to next screen in flow
    } catch (error) {
      // Handle error
    }
  };

  return (
    <View style={styles.container}>
      <TextInput
        value={email}
        onChangeText={setEmail}
        placeholder="Email"
      />
      <TextInput
        value={password}
        onChangeText={setPassword}
        placeholder="Password"
        secureTextEntry
      />
      <Button title="Login" onPress={handleLogin} />
      <Button title="Back" onPress={onBack} />
    </View>
  );
}

Benefits

  • Hybrid Flows: Combine no-code screens with custom native screens
  • Seamless Transitions: Native screens animate in/out matching your flow animations
  • Full Control: Access to all native capabilities while maintaining remote configurability
  • Dynamic Routing: Configure navigation destinations remotely without code changes

Analytics Integration

Muta provides a flexible event system that works with any analytics provider (Mixpanel, AppsFlyer, Amplitude, Firebase, etc.). Events are emitted during key user interactions, allowing you to track and analyze your placement performance.

Tracking All Events

Use the wildcard '*' to listen to all events:

import { Muta } from '@mutalabs/react-native-muta'

// Example with Mixpanel
function MixpanelTracking() {
  useEffect(() => {
    const subscription = Muta.on('*', (event) => {
      mixpanel.track(event.type, {
        placement_id: event.placementId,
        flow_name: event.flowName,
        timestamp: event.timestamp,
        ...event, // includes all event-specific properties
      })
    })

    return () => subscription.remove()
  }, [])
}

// Example with AppsFlyer
function AppsFlyerTracking() {
  useEffect(() => {
    const subscription = Muta.on('*', (event) => {
      appsflyer.logEvent(event.type, {
        placement_id: event.placementId,
        flow_name: event.flowName,
        ...event,
      })
    })

    return () => subscription.remove()
  }, [])
}

Error Handling

Muta will automatically handle errors and prevent the flow from displaying if there are any issues. You can listen for these errors to show appropriate messages to your users.

Example Usage

import { Muta } from '@mutalabs/react-native-muta';

function Onboarding() {
  useEffect(() => {
    // Listen for error events
    const subscription = Muta.on('error', (error) => {
      if (error.type === 'network_error') {
        // Handle network errors (e.g., show "No internet connection" message)
        console.log('Network error:', error.message);
      } else if (error.type === 'placement_error') {
        // Handle placement errors (e.g., show "Invalid placement ID" message)
        console.log('Placement error:', error.message);
      }
    });

    // Show the placement
    Muta.displayPlacement({
      placementId: 'your-placement-id',
      bgColor: '#000000'
    });

    return () => subscription.remove();
  }, []);

  return <View style={{ flex: 1 }} />;
}

Error Types

  1. Network Errors

    • Emitted when there's no internet connection
    • The flow will not display
    • You should show an appropriate "No internet connection" message
  2. Placement Errors

    • Emitted when there's an issue with the placement ID or API key, typically this is when a key or placement does not match what is in the web app or has been deleted
    • The flow will not display
    • You should show an appropriate error message to the user

Error Event Structure

// Network Error
{
  type: 'network_error',
  message: string,      // Error message
  timestamp: number     // Unix timestamp in ms
}

// Placement Error
{
  type: 'placement_error',
  message: string,      // Error message
  code: string,         // Error code
  timestamp: number,    // Unix timestamp in ms
  placementId: string   // ID of the placement that failed
}

Available Events

  1. Flow Started (flow_started) Emitted when a placement flow begins displaying.
{
  type: 'flow_started',
  timestamp: number,      // Unix timestamp in ms
  placementId: string,    // ID of the placement
  flowName?: string,      // Name of the flow (if configured)
  totalScreens: number    // Total number of screens in flow
}
  1. Screen Viewed (screen_viewed) Emitted when a user views a new screen in the flow.
{
  type: 'screen_viewed',
  timestamp: number,
  placementId: string,
  flowName?: string,
  screenIndex: number,    // Zero-based index of current screen
  totalScreens: number,   // Total number of screens in flow
  screenName?: string     // Name of the screen (if configured)
}
  1. Flow Completed (flow_completed) Emitted when a user successfully finishes the flow.
{
  type: 'flow_completed',
  timestamp: number,      // Unix timestamp in ms
  placementId: string,    // ID of the placement
  flowName: string,       // Name of the flow
  screenIndex: number,    // Index of final screen
  totalScreens: number,   // Total screens in flow
  screenName: string,     // Name of final screen
  eventData?: {           // Contains all collected variables
    variables: Record<string, any>  // Variable IDs mapped to their values
  }
}

Variable Structure

When a flow completes, eventData.variables contains an object where:

  • Key: Variable ID (e.g., var_1758310510757_as20wkeo4)
  • Value: Object containing:
    • id (string): Unique variable identifier
    • name (string): Human-readable variable name from the Muta editor
    • type (string): Variable type (e.g., "text")
    • value (any): The collected value
    • defaultValue (any): The default value

Example:

Muta.on('flow_completed', (event) => {
  if (event.eventData?.variables) {
    Object.entries(event.eventData.variables).forEach(([varId, varData]) => {
      console.log(`Variable '${varData.name}': ${varData.value}`)
    })
  }
})

Output:

Variable 'MultipleChoice1': Family
Variable 'MultipleChoice2': Fluent
  1. Flow Abandoned (flow_abandoned) Emitted when a user exits the flow before completion.
{
  type: 'flow_abandoned',
  timestamp: number,
  placementId: string,
  flowName?: string,
  screenIndex: number,    // Index of last viewed screen
  totalScreens: number,   // Total screens in flow
  lastScreenIndex: number, // Index of last viewed screen
  screenName?: string     // Name of last viewed screen
}
  1. Custom Events (configured in web editor) Emitted when user triggers actions configured with "Emit Event" behavior.
{
  type: string,         // Your custom event name
  timestamp: number,    // Unix timestamp in ms
  placementId: string,  // ID of the placement
  flowName?: string,    // Name of the flow
  screenIndex?: number, // Current screen index
  eventData?: any       // Custom data configured in editor
}
  1. Error Events (error) Emitted when there are network or placement errors.
// Network Error
{
  type: 'network_error',
  message: string,      // Error message
  timestamp: number     // Unix timestamp in ms
}

// Placement Error
{
  type: 'placement_error',
  message: string,      // Error message
  code: string,         // Error code
  timestamp: number,    // Unix timestamp in ms
  placementId: string   // ID of the placement that failed
}

Practical Example with Custom Events

Here's an example of using custom events to handle user interactions:

import { Muta } from '@mutalabs/react-native-muta'

function OnboardingWithCustomEvents() {
  useEffect(() => {
    // Listen for flow completion with all collected variables
    const completionListener = Muta.on('flow_completed', (event) => {
      // Access all variables collected during the flow
      if (event.eventData?.variables) {
        const { user_name, user_email, selected_plan, preferences } = event.eventData.variables

        console.log('Flow completed with data:', {
          name: user_name,
          email: user_email,
          plan: selected_plan,
          preferences: preferences
        })

        // Save to your backend
        saveOnboardingData(event.eventData.variables)
      }
    })

    // Listen for form submission event
    const formListener = Muta.on('form_submitted', (event) => {
      // Access the form data from eventData
      const { name, email, preferences } = event.eventData

      console.log('User submitted:', { name, email, preferences })

      // Save to your backend
      saveUserProfile({ name, email, preferences })
    })

    // Listen for plan selection
    const planListener = Muta.on('plan_selected', (event) => {
      const { planType, price } = event.eventData

      // Handle plan selection
      if (planType === 'premium') {
        navigateToPremiumSignup()
      }
    })

    return () => {
      completionListener.remove()
      formListener.remove()
      planListener.remove()
    }
  }, [])

  return null
}