Skip to main content
Code Screens enable you to seamlessly integrate custom native React components into your remotely configured Muta flows. This powerful feature allows you to combine the flexibility of no-code flow management with the full capabilities of native development.

Overview

Code Screens act as placeholders in your Muta flows that are replaced with native React components at runtime. This enables you to:
  • Add authentication screens to your onboarding flows
  • Integrate payment processing directly in your flows
  • Include complex native UI that requires custom logic
  • Maintain remote configurability while using native code

How It Works

1

Create a Code Screen

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 Navigation

Set up the continue and back navigation destinations for your code screen
3

Inject Components

Pass your React components when displaying the placement in your app

Implementation

Basic Setup

Pass your custom components when calling displayPlacement:
import { Muta } from '@mutalabs/react-native-muta';
import { LoginScreen } from './screens/LoginScreen';
import { PaymentScreen } from './screens/PaymentScreen';

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

Component Interface

Your injected components receive the following props:
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
}

Example Component

Here’s a complete example of a login screen component:
import React, { useState } from 'react';
import { View, TextInput, Button, StyleSheet, Text } from 'react-native';

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

  const handleLogin = async () => {
    setError('');
    setLoading(true);

    try {
      // Your authentication logic
      const response = await fetch('https://api.example.com/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password }),
      });

      if (response.ok) {
        const data = await response.json();
        // Store auth token, update user state, etc.
        await AsyncStorage.setItem('authToken', data.token);

        // Navigate to the next screen in the flow
        onContinue();
      } else {
        setError('Invalid email or password');
      }
    } catch (err) {
      setError('Network error. Please try again.');
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Welcome Back</Text>

      <TextInput
        style={styles.input}
        value={email}
        onChangeText={setEmail}
        placeholder="Email"
        keyboardType="email-address"
        autoCapitalize="none"
        editable={!loading}
      />

      <TextInput
        style={styles.input}
        value={password}
        onChangeText={setPassword}
        placeholder="Password"
        secureTextEntry
        editable={!loading}
      />

      {error ? <Text style={styles.error}>{error}</Text> : null}

      <Button
        title={loading ? "Logging in..." : "Login"}
        onPress={handleLogin}
        disabled={loading || !email || !password}
      />

      <Button
        title="Back"
        onPress={onBack}
        disabled={loading}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    justifyContent: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 30,
    textAlign: 'center',
  },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    padding: 15,
    marginBottom: 15,
    borderRadius: 8,
  },
  error: {
    color: 'red',
    marginBottom: 15,
    textAlign: 'center',
  },
});

Animations

Code screens automatically animate in and out matching your flow’s configured animations:
  • Entry Animation: Matches the animation used when navigating TO the code screen
  • Exit Animation: Uses the configured continue/back animation settings
  • Seamless Transitions: Native screens blend smoothly with your flow screens

Use Cases

Authentication Flows

Integrate login, signup, or authentication screens directly into your onboarding:
injectedScreens: [
  { screenName: 'Login', component: LoginScreen },
  { screenName: 'Signup', component: SignupScreen },
  { screenName: 'Verify Email', component: VerifyEmailScreen },
]

Payment Integration

Add payment processing without breaking the flow experience:
injectedScreens: [
  { screenName: 'Payment Method', component: PaymentMethodScreen },
  { screenName: 'Billing Details', component: BillingDetailsScreen },
  { screenName: 'Confirm Purchase', component: ConfirmPurchaseScreen },
]

Complex Forms

Handle multi-step forms or complex data entry:
injectedScreens: [
  { screenName: 'Profile Setup', component: ProfileSetupScreen },
  { screenName: 'Preferences', component: PreferencesScreen },
  { screenName: 'Location Permissions', component: LocationPermissionScreen },
]

Best Practices

  • Use descriptive, unique names for your code screens
  • Keep names consistent between the Muta editor and your code
  • Avoid special characters in screen names
  • Always handle errors gracefully in your components
  • Provide clear feedback to users
  • Consider offering retry options for network failures
  • Keep components lightweight and focused
  • Avoid heavy computations in render methods
  • Use React.memo for performance-critical components

Troubleshooting

Screen names must match exactly between the Muta editor and your injected screens configuration. A mismatch will result in the code screen not displaying.

Common Issues

Code screen not appearing:
  • Verify the screen name matches exactly (case-sensitive)
  • Ensure the component is properly imported
  • Check that injectedScreens is passed to displayPlacement
Navigation not working:
  • Confirm you’re calling onContinue() or onBack()
  • Check that navigation destinations are configured in the editor
  • Ensure no JavaScript errors are preventing execution
Animation issues:
  • Verify presentationType is set appropriately
  • Check that animation durations are reasonable (200-500ms)
  • Ensure components render quickly to avoid animation lag

Advanced Usage

Accessing Flow Variables

Code screens can read and react to flow variables:
function PaymentScreen({ variables, onContinue }: CodeScreenProps) {
  // Find specific variable by ID
  const planType = variables.find(v => v.id === 'selected_plan')?.value;
  const userEmail = variables.find(v => v.id === 'user_email')?.value;

  // Use variables to customize the screen
  const price = planType === 'premium' ? '$9.99' : '$4.99';

  // ... rest of component
}

Sequential Code Screens

You can navigate directly from one code screen to another:
// In Muta editor: Login Screen → Verification Screen → Welcome Screen

injectedScreens: [
  { screenName: 'Login Screen', component: LoginScreen },
  { screenName: 'Verification Screen', component: VerificationScreen },
  // Welcome Screen is a regular Muta screen
]
The SDK handles transitions smoothly between consecutive code screens without any special configuration.