Skip to main content

Customization

Learn how to customize the native error screens and behavior on both iOS and Android platforms.

Custom Native Error Screen (Android)​

Android provides multiple ways to customize the error screen that appears when a native exception occurs.

Method 1: Custom Exception Handler Interface​

Create a custom handler by implementing the NativeExceptionHandlerIfc interface:

// In your MainApplication.kt
import com.globalexceptionhandler.GlobalExceptionHandlerModule
import com.globalexceptionhandler.NativeExceptionHandlerIfc

class MainApplication : Application(), ReactApplication {
override fun onCreate() {
super.onCreate()

// Set custom native exception handler
GlobalExceptionHandlerModule.setNativeExceptionHandler(
object : NativeExceptionHandlerIfc {
override fun handleNativeException(
thread: Thread,
throwable: Throwable,
originalHandler: Thread.UncaughtExceptionHandler?
) {
// Custom handling logic
// - Send to analytics
// - Show custom UI
// - Clean up resources

// Log the error
Log.e("CustomHandler", "Native exception occurred", throwable)

// Optionally call original handler
originalHandler?.uncaughtException(thread, throwable)
}
}
)
}
}

Method 2: Custom Error Activity​

Replace the default error screen with your own Activity:

Step 1: Create your custom error activity:

// CustomErrorActivity.kt
import android.app.Activity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView

class CustomErrorActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_custom_error)

// Get error message from intent
val errorMessage = intent.getStringExtra("error")

findViewById<TextView>(R.id.errorText).text = errorMessage

findViewById<Button>(R.id.restartButton).setOnClickListener {
// Restart app logic
val intent = packageManager.getLaunchIntentForPackage(packageName)
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(intent)
finish()
}
}
}

Step 2: Create layout file res/layout/activity_custom_error.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="24dp">

<TextView
android:id="@+id/errorText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_marginBottom="24dp"/>

<Button
android:id="@+id/restartButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Restart App"/>
</LinearLayout>

Step 3: Register the custom activity in MainApplication.kt:

import com.globalexceptionhandler.GlobalExceptionHandlerModule

class MainApplication : Application(), ReactApplication {
override fun onCreate() {
super.onCreate()

GlobalExceptionHandlerModule.replaceErrorScreenActivityClass(
CustomErrorActivity::class.java
)
}
}

Step 4: Add activity to AndroidManifest.xml:

<activity
android:name=".CustomErrorActivity"
android:exported="false"
android:theme="@style/Theme.AppCompat" />

Custom Native Error Screen (iOS)​

For iOS, customize the error handler in your AppDelegate.m or AppDelegate.mm:

Basic Custom Alert​

#import "GlobalExceptionHandler.h"

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Your existing setup...

// Custom native exception handler
[GlobalExceptionHandler replaceNativeExceptionHandlerBlock:^(
NSException *exception,
NSString *readeableException
) {
// Create custom alert
UIAlertController* alert = [UIAlertController
alertControllerWithTitle:@"App Error"
message:[NSString stringWithFormat:
@"An unexpected error occurred.\n\n%@",
readeableException
]
preferredStyle:UIAlertControllerStyleAlert];

// Add action
UIAlertAction* closeAction = [UIAlertAction
actionWithTitle:@"Close App"
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action) {
[GlobalExceptionHandler releaseExceptionHold];
}];

[alert addAction:closeAction];

// Present the alert
[self.window.rootViewController
presentViewController:alert
animated:YES
completion:nil];

// Auto-close after 4 seconds
[NSTimer scheduledTimerWithTimeInterval:4.0
target:[GlobalExceptionHandler class]
selector:@selector(releaseExceptionHold)
userInfo:nil
repeats:NO];
}];

return YES;
}

Custom Error View Controller​

// ErrorViewController.h
@interface ErrorViewController : UIViewController
@property (nonatomic, strong) NSString *errorMessage;
@end

// ErrorViewController.m
@implementation ErrorViewController

- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];

// Add UI elements
UILabel *titleLabel = [[UILabel alloc] initWithFrame:
CGRectMake(20, 100, self.view.bounds.size.width - 40, 40)];
titleLabel.text = @"An Error Occurred";
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.font = [UIFont boldSystemFontOfSize:24];
[self.view addSubview:titleLabel];

UILabel *messageLabel = [[UILabel alloc] initWithFrame:
CGRectMake(20, 160, self.view.bounds.size.width - 40, 200)];
messageLabel.text = self.errorMessage;
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:messageLabel];

UIButton *closeButton = [UIButton buttonWithType:UIButtonTypeSystem];
closeButton.frame = CGRectMake(
(self.view.bounds.size.width - 200) / 2,
400,
200,
44
);
[closeButton setTitle:@"Close App" forState:UIControlStateNormal];
[closeButton addTarget:self
action:@selector(closeApp)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:closeButton];
}

- (void)closeApp {
[GlobalExceptionHandler releaseExceptionHold];
}

@end

// In AppDelegate.m
[GlobalExceptionHandler replaceNativeExceptionHandlerBlock:^(
NSException *exception,
NSString *readeableException
) {
ErrorViewController *errorVC = [[ErrorViewController alloc] init];
errorVC.errorMessage = readeableException;

[self.window.rootViewController
presentViewController:errorVC
animated:YES
completion:nil];
}];

Platform Considerations​

iOS Limitations​

  • Cannot restart app programmatically after native crash
  • UI becomes unstable during native exceptions
  • Must call releaseExceptionHold() to close the app
  • Best practice: Show informative message and let user restart

Android Capabilities​

  • Can restart app after native crash
  • More stable during native exceptions
  • Built-in restart functionality available
  • More customization options

Best Practices​

Keep It Simple​

Error handlers run in an unstable state. Keep your custom UI simple:

  • Avoid complex animations
  • Minimize network calls
  • Use basic UI components
  • Provide clear messaging

Test Thoroughly​

Test your custom error screens with different crash scenarios:

import { simulateNativeCrash, CrashType } from 'react-native-global-exception-handler';

// Test different crash types
simulateNativeCrash(CrashType.array_bounds);
simulateNativeCrash(CrashType.memory_access);

Provide User Actions​

Always give users a way forward:

  • "Restart App" button (Android)
  • "Close App" button (iOS)
  • Contact support option
  • Clear error description

Example: Branded Error Screen​

Create a consistent error experience that matches your app's branding:

// Android branded error screen
class BrandedErrorActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Use your app's theme and styling
setTheme(R.style.AppTheme)

val layout = LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
setPadding(48, 48, 48, 48)
setBackgroundColor(getColor(R.color.brandBackground))
}

// Brand logo
ImageView(this).apply {
setImageResource(R.drawable.ic_app_logo)
layoutParams = LinearLayout.LayoutParams(200, 200)
layout.addView(this)
}

// Error message
TextView(this).apply {
text = "Something went wrong"
textSize = 20f
setTextColor(getColor(R.color.brandPrimary))
setPadding(0, 32, 0, 16)
layout.addView(this)
}

// Restart button
Button(this).apply {
text = "Restart App"
setOnClickListener { restartApp() }
layout.addView(this)
}

setContentView(layout)
}

private fun restartApp() {
val intent = packageManager.getLaunchIntentForPackage(packageName)
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(intent)
finish()
}
}