@hexhad/react-native-global-modal
v0.3.0
Published
A custom-built component that replicates the functionality of the React Native Alert, providing the same behavior with additional styling and customization options.
Readme
Features
- 🚀 Global Access - Show alerts from anywhere in your app
- 📱 React Native Optimized - Built specifically for React Native
- 🎯 Priority System - High and low priority alert queuing with LIFO processing
- 🎨 Fully Customizable - Custom styling and alert components
- ⚡ TypeScript Support - Full type safety out of the box
- 🔄 Smart Queue Management - Intelligent alert queuing with priority handling
- 🪝 React Hooks - Modern React patterns with useGlobalAlert hook
- 🎭 Multiple Alert Types - Error, Warning, Success, Info, Notice, Question, Loading, and Tips
- ⚙️ Flexible Configuration - Optional priority enforcement and backdrop dismiss
- 📚 Multiple Alerts - Show multiple alerts in sequence with
showMultiple - 🎨 Button Customization - Rich button styling and behavior options
Installation
npm install @hexhad/react-native-global-modalor
yarn add @hexhad/react-native-global-modalQuick Start
1. Setup the Provider
Wrap your app with GlobalAlertProvider and provide your custom alert modal component:
import React from 'react';
import { GlobalAlertProvider, GlobalAlert } from '@hexhad/react-native-global-modal';
import CustomAlertModal from './components/CustomAlertModal';
export default function App() {
return (
<GlobalAlertProvider
AlertModal={CustomAlertModal}
ignorePriority={false} // Optional: set to true to always show latest alert
>
{/* Your app content */}
</GlobalAlertProvider>
);
}2. Create Your Alert Modal Component
import React from 'react';
import { Modal, View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import type { AlertModalProps } from '@hexhad/react-native-global-modal';
const CustomAlertModal: React.FC<AlertModalProps> = ({ visible, data, onClose }) => {
if (!data) return null;
return (
<Modal
visible={visible}
transparent
animationType="fade"
onRequestClose={data.backdropDismiss ? onClose : undefined}
>
<TouchableOpacity
style={styles.overlay}
activeOpacity={1}
onPress={data.backdropDismiss ? onClose : undefined}
>
<TouchableOpacity style={styles.container} activeOpacity={1}>
{data.title && <Text style={styles.title}>{data.title}</Text>}
{data.message && <Text style={styles.message}>{data.message}</Text>}
<View style={styles.buttonContainer}>
{data.buttons?.map((button, index) => (
<TouchableOpacity
key={index}
style={[styles.button, button.style, { backgroundColor: getButtonColor(button.variant) }]}
onPress={() => {
button.onPress?.();
if (button.closeOnPress !== false) {
onClose?.();
}
}}
>
<Text style={[styles.buttonText, button.textStyle]}>{button.title}</Text>
</TouchableOpacity>
))}
</View>
</TouchableOpacity>
</TouchableOpacity>
</Modal>
);
};
const getButtonColor = (variant?: string) => {
switch (variant) {
case 'ERROR': return '#FF6B6B';
case 'SUCCESS': return '#51CF66';
case 'WARNING': return '#FFD93D';
default: return '#339AF0';
}
};
const styles = StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
},
container: {
backgroundColor: 'white',
borderRadius: 12,
padding: 20,
margin: 20,
minWidth: 280,
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10,
textAlign: 'center',
},
message: {
fontSize: 16,
marginBottom: 20,
textAlign: 'center',
color: '#666',
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
},
button: {
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 6,
minWidth: 80,
},
buttonText: {
color: 'white',
fontWeight: 'bold',
textAlign: 'center',
},
});
export default CustomAlertModal;3. Show Alerts
Using Global Object (Imperative API)
import { GlobalAlert } from '@hexhad/react-native-global-modal';
// Simple alert
GlobalAlert.show({
title: 'Success!',
message: 'Your action was completed successfully.',
type: GlobalAlert.TYPE.SUCCESS,
buttons: [
{
title: 'OK',
onPress: () => console.log('OK pressed'),
closeOnPress: true, // Default behavior
},
],
});
// High priority alert (interrupts current alerts)
GlobalAlert.show({
title: 'Critical Error',
message: 'Something went wrong!',
priority: GlobalAlert.PRIORITY.HIGH, // High priority
type: GlobalAlert.TYPE.ERROR,
buttons: [
{
title: 'Retry',
onPress: () => {
// Handle retry logic
console.log('Retrying...');
},
},
{
title: 'Cancel',
variant: 'ERROR',
onPress: () => console.log('Cancelled'),
},
],
});
// Alert with backdrop dismiss
GlobalAlert.show({
title: 'Info',
message: 'Tap outside to dismiss',
backdropDismiss: true,
buttons: [
{
title: 'Got it',
closeOnPress: true,
},
],
});
// Show multiple alerts in sequence
GlobalAlert.showMultiple([
{
title: 'First Alert',
message: 'This will show first',
type: GlobalAlert.TYPE.INFO,
buttons: [{ title: 'Next' }],
},
{
title: 'Second Alert',
message: 'This will show after the first is closed',
type: GlobalAlert.TYPE.SUCCESS,
buttons: [{ title: 'Done' }],
}
]);
// Hide current alert
GlobalAlert.hide();
// Clear all alerts (including queue)
GlobalAlert.clearAll();Using React Hook (Declarative API)
import React from 'react';
import { View, Button } from 'react-native';
import { useGlobalAlert } from '@hexhad/react-native-global-modal';
const MyComponent: React.FC = () => {
const alert = useGlobalAlert();
const showConfirmation = () => {
alert.show({
title: 'Confirm Action',
message: 'Are you sure you want to proceed?',
type: alert.TYPE.QUESTION,
priority: alert.PRIORITY.HIGH, // Ensure this shows immediately
buttons: [
{
title: 'Cancel',
onPress: () => console.log('Cancelled'),
closeOnPress: true,
},
{
title: 'Confirm',
variant: 'SUCCESS',
onPress: () => {
console.log('Confirmed!');
// Handle confirmation logic
},
closeOnPress: true,
},
],
});
};
return (
<View>
<Button title="Show Confirmation" onPress={showConfirmation} />
</View>
);
};API Reference
GlobalAlertProvider
Main provider component that manages the global alert system.
Props
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| AlertModal | React.ComponentType<AlertModalProps> | ✅ | - | Your custom alert modal component |
| children | React.ReactNode | ❌ | - | Your app content |
| types | Record<string, string> | ❌ | {} | Custom alert types (merged with defaults) |
| globalAlert | GlobalAlertInstance | ❌ | - | Custom global alert instance |
| ignorePriority | boolean | ❌ | false | If true, always show the latest alert immediately |
AlertData
Configuration object for alert content and behavior.
type AlertData = {
type?: string; // Alert type (ERROR, SUCCESS, etc.)
variant?: string; // Visual variant for buttons/styling
title?: string; // Alert title
message?: string; // Alert message
priority?: number; // Priority: 0 (LOW) or 1 (HIGH)
backdropDismiss?: boolean; // Allow dismissing by tapping backdrop
buttons?: AlertButton[]; // Array of action buttons
onClose?: () => void; // Callback when alert closes
[key: string]: any; // Additional custom properties
};AlertButton
Configuration for alert buttons.
type AlertButton = {
title?: string; // Button text
variant?: string; // Visual variant for styling
onPress?: () => void; // Button press handler
closeOnPress?: boolean; // Whether to close alert on press (default: true)
style?: any; // Custom button styles
textStyle?: any; // Custom button text styles
};AlertModalProps
Props passed to your custom modal component.
type AlertModalProps = {
visible: boolean; // Whether modal should be visible
data: AlertData | null; // Alert configuration data
onClose?: () => void; // Function to close the alert
};GlobalAlert Object
Properties
GlobalAlert.TYPE: Predefined alert typesERROR,WARNING,SUCCESS,INFO,NOTICE,QUESTION,LOADING,TIP
GlobalAlert.PRIORITY: Priority levelsLOW: 0(Low priority - default)HIGH: 1(High priority)
Methods
GlobalAlert.show(options: AlertData): Show an alertGlobalAlert.showMultiple(options: AlertData[]): Show multiple alerts in sequenceGlobalAlert.hide(): Hide the current alertGlobalAlert.clearAll(): Hide current alert and clear the queue
useGlobalAlert Hook
React hook that provides access to the alert system within components.
const alert = useGlobalAlert();
// Returns: { show, showMultiple, hide, clearAll, TYPE, PRIORITY }Note: The hook will throw an error if used outside of GlobalAlertProvider.
Priority System & Queue Management
The alert system uses a sophisticated priority-based queue with LIFO (Last In, First Out) processing:
Priority Levels
- Low Priority (
PRIORITY.LOW = 0): Default priority. Can be interrupted by any alert. - High Priority (
PRIORITY.HIGH = 1): Interrupts low priority alerts and queues when interrupted by other high priority alerts.
Queue Behavior
With ignorePriority={false} (Default)
- Low priority showing + High priority triggered → High priority shows immediately, low priority discarded
- High priority showing + High priority triggered → New alert shows immediately, current alert queued
- High priority showing + Low priority triggered → Low priority shows immediately, high priority alert queued
- Queue processing → Only high priority alerts are preserved in queue, processed in LIFO order
With ignorePriority={true}
- Any alert showing + New alert triggered → New alert shows immediately, current alert queued (regardless of priority)
- Queue processing → All alerts processed in LIFO order
Queue Processing Details
- Queue processing happens 50ms after an alert is hidden
- Only one alert can be shown at a time
- When
ignorePriority={false}, only high priority alerts are kept in the queue - The queue uses LIFO (stack) behavior - most recent alerts show first
Examples
Loading Alert
const showLoading = () => {
GlobalAlert.show({
type: GlobalAlert.TYPE.LOADING,
message: 'Please wait...',
priority: GlobalAlert.PRIORITY.HIGH,
// No buttons = user must wait for programmatic dismissal
});
// Simulate async operation
setTimeout(() => {
GlobalAlert.hide();
// Show completion
GlobalAlert.show({
type: GlobalAlert.TYPE.SUCCESS,
title: 'Complete!',
message: 'Operation finished successfully.',
buttons: [{ title: 'OK' }],
});
}, 3000);
};Custom Properties & Styling
Your alert modal can access any custom properties you pass:
GlobalAlert.show({
title: 'Custom Alert',
message: 'This alert has custom properties',
customIcon: 'star', // Custom property
backgroundColor: '#f0f0f0', // Custom property
animationType: 'bounce', // Custom property
buttons: [
{
title: 'Styled Button',
style: {
backgroundColor: '#FF6B6B',
borderRadius: 20,
paddingHorizontal: 30,
},
textStyle: {
color: 'white',
fontWeight: 'bold',
fontSize: 16,
},
onPress: () => console.log('Custom button pressed'),
},
],
});Error Handling with Retry
const handleApiError = (error: Error, retryFn: () => void) => {
GlobalAlert.show({
title: 'Request Failed',
message: error.message || 'An unexpected error occurred.',
type: GlobalAlert.TYPE.ERROR,
priority: GlobalAlert.PRIORITY.HIGH, // Ensure it shows immediately
buttons: [
{
title: 'Retry',
variant: 'SUCCESS',
onPress: () => {
console.log('Retrying...');
retryFn();
},
},
{
title: 'Cancel',
variant: 'ERROR',
onPress: () => console.log('Operation cancelled'),
},
],
onClose: () => {
console.log('Error alert closed');
},
});
};Confirmation Dialog
const showDeleteConfirmation = (itemName: string, onConfirm: () => void) => {
GlobalAlert.show({
title: 'Delete Item',
message: `Are you sure you want to delete "${itemName}"? This action cannot be undone.`,
type: GlobalAlert.TYPE.QUESTION,
priority: GlobalAlert.PRIORITY.HIGH,
buttons: [
{
title: 'Cancel',
style: { backgroundColor: '#gray' },
onPress: () => console.log('Delete cancelled'),
},
{
title: 'Delete',
variant: 'ERROR',
onPress: () => {
console.log(`Deleting ${itemName}`);
onConfirm();
},
},
],
});
};Multiple Alerts Demo
const showMultipleAlertsDemo = () => {
GlobalAlert.showMultiple([
{
title: 'Welcome!',
message: 'Thank you for using our app',
type: GlobalAlert.TYPE.SUCCESS,
buttons: [{ title: 'Next' }],
},
{
title: 'New Features',
message: 'Check out what\'s new in this version',
type: GlobalAlert.TYPE.INFO,
buttons: [{ title: 'Continue' }],
},
{
title: 'Quick Tip',
message: 'You can customize alerts with your own styling',
type: GlobalAlert.TYPE.TIP,
buttons: [{ title: 'Got it!' }],
},
]);
};Alert Queue Demo
const showQueueDemo = () => {
// Low priority alert
GlobalAlert.show({
title: 'Low Priority 1',
message: 'This is a low priority alert',
priority: GlobalAlert.PRIORITY.LOW,
buttons: [{ title: 'OK' }],
});
// High priority alert (will interrupt)
setTimeout(() => {
GlobalAlert.show({
title: 'High Priority',
message: 'This high priority alert interrupts the low priority one',
priority: GlobalAlert.PRIORITY.HIGH,
buttons: [{ title: 'Got it' }],
});
}, 1000);
// Another high priority (will queue)
setTimeout(() => {
GlobalAlert.show({
title: 'Another High Priority',
message: 'This will be queued and shown after the first high priority alert',
priority: GlobalAlert.PRIORITY.HIGH,
buttons: [{ title: 'Understood' }],
});
}, 2000);
};Advanced Usage
Custom Alert Types
// In your app setup
const customTypes = {
CUSTOM_SUCCESS: 'CUSTOM_SUCCESS',
MAINTENANCE: 'MAINTENANCE',
FEATURE_ANNOUNCEMENT: 'FEATURE_ANNOUNCEMENT',
};
<GlobalAlertProvider
AlertModal={CustomAlertModal}
types={customTypes}
>
{/* Your app */}
</GlobalAlertProvider>
// Usage
GlobalAlert.show({
type: 'CUSTOM_SUCCESS', // or GlobalAlert.TYPE.CUSTOM_SUCCESS
title: 'Feature Updated!',
message: 'Check out the new functionality.',
});Using Custom Global Alert Instance
import { createGlobalAlert } from '@hexhad/react-native-global-modal';
const customAlert = createGlobalAlert({
SPECIAL: 'SPECIAL',
NOTIFICATION: 'NOTIFICATION',
});
// Use the custom instance
customAlert.show({
type: customAlert.TYPE.SPECIAL,
title: 'Special Alert',
message: 'This uses a custom alert instance',
});Error Boundaries Integration
class ErrorBoundary extends React.Component {
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
GlobalAlert.show({
title: 'Application Error',
message: 'Something went wrong. The app will reload.',
type: GlobalAlert.TYPE.ERROR,
priority: GlobalAlert.PRIORITY.HIGH,
buttons: [
{
title: 'Reload App',
onPress: () => {
// Reload or restart logic
window.location.reload();
},
},
],
});
}
render() {
return this.props.children;
}
}TypeScript Support
The library is fully typed with comprehensive TypeScript definitions:
import type {
AlertData,
AlertButton,
AlertModalProps,
AlertModalRef,
GlobalAlertInstance,
GlobalAlertProviderProps,
Priority
} from '@hexhad/react-native-global-modal';
// Example: Typed custom alert function
const showTypedAlert = (config: AlertData): void => {
GlobalAlert.show({
...config,
priority: config.priority ?? GlobalAlert.PRIORITY.LOW, // Default to low priority
});
};Best Practices
- Use High Priority Sparingly - Reserve
PRIORITY.HIGHfor critical alerts that must interrupt the user - Provide Clear Actions - Always include actionable buttons unless the alert is purely informational
- Handle onClose Properly - Use
onClosecallback for cleanup when alerts are dismissed - Custom Properties - Leverage custom properties to pass additional data to your modal component
- Error Handling - Always handle button press errors gracefully
- Queue Management - Consider using
ignorePriority={true}for simpler queue behavior if priority handling isn't needed - Backdrop Dismiss - Use
backdropDismissfor non-critical alerts to improve UX - Multiple Alerts - Use
showMultiple()for onboarding flows or sequential information - Button Variants - Use consistent button variants across your app for better UX
Troubleshooting
Common Issues
"GlobalAlert.show called before GlobalAlertProvider mounted"
- Ensure
GlobalAlertProviderwraps your app at the root level - Don't call
GlobalAlert.show()during app initialization before the provider mounts
- Ensure
"useGlobalAlert must be used within a GlobalAlertProvider"
- The
useGlobalAlert()hook can only be used inside components wrapped byGlobalAlertProvider
- The
Alerts not showing
- Check that your
AlertModalcomponent properly handles thevisibleprop - Ensure your modal component renders when
datais not null
- Check that your
Queue not working as expected
- Review the priority system documentation
- Consider using
ignorePriority={true}for simpler behavior
Backdrop dismiss not working
- Ensure your modal handles
onRequestCloseand touch events on the backdrop - Check that
backdropDismissis set totruein your alert data
- Ensure your modal handles
Contributing
See the contributing guide to learn how to contribute to the repository and the development workflow.
License
MIT
Made with ❤️ for the React Native community
