A framework for building native apps using React
Overall
score
100%
Evaluation — 100%
↑ 1.06xAgent success when using this tile
React Native provides comprehensive APIs for handling user interactions, displaying system dialogs, accessing device feedback capabilities, and platform-specific UI components.
npm install react-nativeDisplay native alert dialogs with customizable buttons and actions.
// ESM
import {Alert} from 'react-native';
// CommonJS
const {Alert} = require('react-native');
// Basic alert
Alert.alert('Alert Title', 'Alert message');
// Alert with single button
Alert.alert(
'Confirmation',
'Are you sure you want to continue?',
[{text: 'OK', onPress: () => console.log('OK pressed')}]
);
// Alert with multiple buttons
Alert.alert(
'Save Changes',
'Do you want to save your changes before leaving?',
[
{text: 'Cancel', style: 'cancel'},
{text: 'Don\'t Save', style: 'destructive', onPress: () => discardChanges()},
{text: 'Save', onPress: () => saveChanges()},
]
);
// Alert with custom button styles
Alert.alert(
'Delete Item',
'This action cannot be undone.',
[
{text: 'Cancel', style: 'cancel'},
{
text: 'Delete',
style: 'destructive',
onPress: () => deleteItem(),
},
],
{
cancelable: true, // Android: allow dismissing by tapping outside
onDismiss: () => console.log('Alert dismissed'),
}
);
// Prompt for text input (iOS only)
Alert.prompt(
'Enter Name',
'Please enter your name:',
[
{text: 'Cancel', style: 'cancel'},
{
text: 'OK',
onPress: (text) => {
console.log('Entered text:', text);
setUserName(text);
},
},
],
'plain-text', // Input type
'Default text' // Default value
);
// Secure text prompt (iOS only)
Alert.prompt(
'Enter Password',
'Please enter your password:',
[
{text: 'Cancel', style: 'cancel'},
{text: 'Login', onPress: (password) => login(password)},
],
'secure-text'
);
// Login prompt with username and password (iOS only)
Alert.prompt(
'Login',
'Please enter your credentials:',
[
{text: 'Cancel', style: 'cancel'},
{
text: 'Login',
onPress: (username, password) => {
login(username, password);
},
},
],
'login-password'
);
// Async alert with promise
const showAsyncAlert = () => {
return new Promise((resolve) => {
Alert.alert(
'Confirmation',
'Do you want to proceed?',
[
{text: 'No', onPress: () => resolve(false)},
{text: 'Yes', onPress: () => resolve(true)},
]
);
});
};
// Usage
const handleAction = async () => {
const confirmed = await showAsyncAlert();
if (confirmed) {
performAction();
}
};
// Custom alert hook
function useAlert() {
const showAlert = useCallback((title, message, buttons = []) => {
Alert.alert(title, message, buttons);
}, []);
const showConfirmation = useCallback((title, message, onConfirm, onCancel) => {
Alert.alert(title, message, [
{text: 'Cancel', style: 'cancel', onPress: onCancel},
{text: 'OK', onPress: onConfirm},
]);
}, []);
const showError = useCallback((message) => {
Alert.alert('Error', message, [{text: 'OK'}]);
}, []);
const showSuccess = useCallback((message) => {
Alert.alert('Success', message, [{text: 'OK'}]);
}, []);
return {
showAlert,
showConfirmation,
showError,
showSuccess,
};
}interface AlertStatic {
// Basic alert
alert(
title: string,
message?: string,
buttons?: AlertButton[],
options?: AlertOptions
): void;
// Text input prompt (iOS only)
prompt(
title: string,
message?: string,
callbackOrButtons?: ((text: string) => void) | AlertButton[],
type?: AlertType,
defaultValue?: string,
keyboardType?: string
): void;
}
interface AlertButton {
text?: string;
onPress?: (value?: string) => void;
style?: 'default' | 'cancel' | 'destructive';
}
interface AlertOptions {
cancelable?: boolean; // Android only
onDismiss?: () => void; // Android only
userInterfaceStyle?: 'unspecified' | 'light' | 'dark'; // iOS only
}
type AlertType = 'default' | 'plain-text' | 'secure-text' | 'login-password';Trigger device vibration patterns for haptic feedback.
import {Vibration} from 'react-native';
// Basic vibration (400ms on iOS, default pattern on Android)
Vibration.vibrate();
// Custom duration (iOS only, Android uses default)
Vibration.vibrate(1000); // 1 second
// Vibration pattern [delay, vibrate, delay, vibrate, ...]
const pattern = [0, 1000, 1000, 1000]; // Vibrate 1s, pause 1s, vibrate 1s
Vibration.vibrate(pattern);
// Repeat vibration pattern
Vibration.vibrate(pattern, true); // Repeat infinitely
// Stop vibration
Vibration.cancel();
// Predefined patterns
const vibrationPatterns = {
short: [0, 200],
medium: [0, 500],
long: [0, 1000],
double: [0, 200, 100, 200],
triple: [0, 200, 100, 200, 100, 200],
heartbeat: [0, 50, 50, 50, 50, 400, 50, 400],
sos: [0, 200, 100, 200, 100, 200, 200, 400, 100, 400, 100, 400, 200, 200, 100, 200, 100, 200],
};
// Vibration feedback component
function VibrationFeedback({pattern = 'short', children, onPress}) {
const handlePress = () => {
Vibration.vibrate(vibrationPatterns[pattern]);
onPress?.();
};
return (
<TouchableOpacity onPress={handlePress}>
{children}
</TouchableOpacity>
);
}
// Haptic feedback for different interactions
const hapticFeedback = {
success: () => Vibration.vibrate([0, 50, 50, 50]),
error: () => Vibration.vibrate([0, 100, 100, 100, 100, 100]),
warning: () => Vibration.vibrate([0, 200, 100, 200]),
selection: () => Vibration.vibrate([0, 10]),
impact: () => Vibration.vibrate([0, 30]),
notification: () => Vibration.vibrate([0, 50, 50, 100]),
};
// Form validation with haptic feedback
function FormWithHaptics() {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
const validateEmail = (value) => {
if (!value.includes('@')) {
setError('Invalid email');
hapticFeedback.error();
} else {
setError('');
hapticFeedback.success();
}
};
return (
<View>
<TextInput
value={email}
onChangeText={setEmail}
onBlur={() => validateEmail(email)}
placeholder="Email"
/>
{error && <Text style={styles.error}>{error}</Text>}
</View>
);
}
// Game interactions with vibration
function GameButton({onPress, disabled}) {
const handlePress = () => {
if (disabled) {
hapticFeedback.error();
return;
}
hapticFeedback.impact();
onPress();
};
return (
<TouchableOpacity onPress={handlePress} disabled={disabled}>
<Text>Game Action</Text>
</TouchableOpacity>
);
}
// Settings for vibration preferences
function VibrationSettings() {
const [vibrationEnabled, setVibrationEnabled] = useState(true);
const toggleVibration = (enabled) => {
setVibrationEnabled(enabled);
if (enabled) {
hapticFeedback.success();
}
};
return (
<View style={styles.settingRow}>
<Text>Enable Vibration</Text>
<Switch value={vibrationEnabled} onValueChange={toggleVibration} />
</View>
);
}interface VibrationStatic {
// Trigger vibration
vibrate(pattern?: number | number[], repeat?: boolean): void;
// Stop vibration
cancel(): void;
}Display native iOS action sheets with customizable options.
import {ActionSheetIOS} from 'react-native';
// Basic action sheet (iOS only)
const showActionSheet = () => {
ActionSheetIOS.showActionSheetWithOptions(
{
title: 'Choose an option',
message: 'Select one of the following options:',
options: ['Cancel', 'Option 1', 'Option 2', 'Option 3'],
cancelButtonIndex: 0,
},
(buttonIndex) => {
if (buttonIndex === 1) {
console.log('Option 1 selected');
} else if (buttonIndex === 2) {
console.log('Option 2 selected');
} else if (buttonIndex === 3) {
console.log('Option 3 selected');
}
}
);
};
// Action sheet with destructive option
const showDestructiveActionSheet = () => {
ActionSheetIOS.showActionSheetWithOptions(
{
title: 'Delete Item',
message: 'This action cannot be undone',
options: ['Cancel', 'Delete Item'],
destructiveButtonIndex: 1,
cancelButtonIndex: 0,
},
(buttonIndex) => {
if (buttonIndex === 1) {
deleteItem();
}
}
);
};
// Share action sheet
const showShareSheet = () => {
ActionSheetIOS.showShareActionSheetWithOptions(
{
url: 'https://example.com',
message: 'Check out this amazing app!',
subject: 'App Recommendation', // Email subject
},
(error) => {
console.error('Share failed:', error);
},
(success, method) => {
if (success) {
console.log(`Shared via ${method}`);
}
}
);
};
// Photo selection action sheet
const showPhotoActionSheet = () => {
ActionSheetIOS.showActionSheetWithOptions(
{
title: 'Select Photo',
options: ['Cancel', 'Camera', 'Photo Library'],
cancelButtonIndex: 0,
},
(buttonIndex) => {
switch (buttonIndex) {
case 1:
openCamera();
break;
case 2:
openPhotoLibrary();
break;
}
}
);
};
// Custom hook for action sheets
function useActionSheet() {
const showOptions = useCallback((options, onSelect) => {
if (Platform.OS !== 'ios') {
// Fallback for non-iOS platforms
Alert.alert(
options.title,
options.message,
options.options.map((option, index) => ({
text: option,
onPress: () => onSelect(index),
style: index === options.cancelButtonIndex ? 'cancel' :
index === options.destructiveButtonIndex ? 'destructive' : 'default',
}))
);
return;
}
ActionSheetIOS.showActionSheetWithOptions(options, onSelect);
}, []);
return {showOptions};
}
// Settings menu with action sheet
function SettingsMenu() {
const {showOptions} = useActionSheet();
const showSettingsActions = () => {
showOptions(
{
title: 'Settings',
options: ['Cancel', 'Edit Profile', 'Privacy Settings', 'Sign Out'],
cancelButtonIndex: 0,
destructiveButtonIndex: 3,
},
(buttonIndex) => {
switch (buttonIndex) {
case 1:
navigation.navigate('EditProfile');
break;
case 2:
navigation.navigate('PrivacySettings');
break;
case 3:
signOut();
break;
}
}
);
};
return (
<TouchableOpacity onPress={showSettingsActions}>
<Text>Settings</Text>
</TouchableOpacity>
);
}interface ActionSheetIOSStatic {
// Show action sheet
showActionSheetWithOptions(
options: ActionSheetOptions,
callback: (buttonIndex: number) => void
): void;
// Show share sheet (iOS only)
showShareActionSheetWithOptions(
options: ShareActionSheetOptions,
failureCallback: (error: Error) => void,
successCallback: (success: boolean, method: string) => void
): void;
}
interface ActionSheetOptions {
title?: string;
message?: string;
options: string[];
cancelButtonIndex?: number;
destructiveButtonIndex?: number | number[];
anchor?: number; // iPad popover anchor
tintColor?: string;
userInterfaceStyle?: 'light' | 'dark';
}
interface ShareActionSheetOptions {
message?: string;
url?: string;
subject?: string;
anchor?: number; // iPad popover anchor
tintColor?: string;
excludedActivityTypes?: string[];
}Display native Android toast messages for brief notifications.
import {ToastAndroid} from 'react-native';
// Basic toast (Android only)
const showToast = (message) => {
if (Platform.OS === 'android') {
ToastAndroid.show(message, ToastAndroid.SHORT);
}
};
// Different toast durations
const showShortToast = (message) => {
ToastAndroid.show(message, ToastAndroid.SHORT); // ~2 seconds
};
const showLongToast = (message) => {
ToastAndroid.show(message, ToastAndroid.LONG); // ~3.5 seconds
};
// Toast with gravity positioning
const showPositionedToast = (message) => {
ToastAndroid.showWithGravity(
message,
ToastAndroid.SHORT,
ToastAndroid.CENTER
);
};
// Toast with custom positioning
const showCustomPositionToast = (message) => {
ToastAndroid.showWithGravityAndOffset(
message,
ToastAndroid.SHORT,
ToastAndroid.BOTTOM,
25, // x offset
50 // y offset
);
};
// Cross-platform toast utility
const toast = {
show: (message, duration = 'short') => {
if (Platform.OS === 'android') {
const androidDuration = duration === 'long'
? ToastAndroid.LONG
: ToastAndroid.SHORT;
ToastAndroid.show(message, androidDuration);
} else {
// iOS fallback using Alert
Alert.alert('', message);
}
},
success: (message) => {
toast.show(`✅ ${message}`);
},
error: (message) => {
toast.show(`❌ ${message}`);
},
warning: (message) => {
toast.show(`⚠️ ${message}`);
},
info: (message) => {
toast.show(`ℹ️ ${message}`);
},
};
// Form submission with toast feedback
function FormWithToast() {
const [loading, setLoading] = useState(false);
const handleSubmit = async (formData) => {
setLoading(true);
try {
await submitForm(formData);
toast.success('Form submitted successfully!');
} catch (error) {
toast.error('Failed to submit form');
} finally {
setLoading(false);
}
};
return (
<View>
{/* Form content */}
<Button
title="Submit"
onPress={handleSubmit}
disabled={loading}
/>
</View>
);
}
// Network status with toast
function NetworkStatusToast() {
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
if (!state.isConnected) {
toast.warning('No internet connection');
} else if (state.isConnected) {
toast.success('Connected to internet');
}
});
return unsubscribe;
}, []);
return null;
}
// Custom toast hook
function useToast() {
const show = useCallback((message, options = {}) => {
if (Platform.OS === 'android') {
const duration = options.duration === 'long'
? ToastAndroid.LONG
: ToastAndroid.SHORT;
const gravity = options.position === 'top'
? ToastAndroid.TOP
: options.position === 'center'
? ToastAndroid.CENTER
: ToastAndroid.BOTTOM;
ToastAndroid.showWithGravity(message, duration, gravity);
} else {
// iOS fallback
Alert.alert('', message);
}
}, []);
return {
show,
success: (message) => show(`✅ ${message}`),
error: (message) => show(`❌ ${message}`),
warning: (message) => show(`⚠️ ${message}`),
info: (message) => show(`ℹ️ ${message}`),
};
}interface ToastAndroidStatic {
// Constants
SHORT: number;
LONG: number;
TOP: number;
BOTTOM: number;
CENTER: number;
// Show methods
show(message: string, duration: number): void;
showWithGravity(message: string, duration: number, gravity: number): void;
showWithGravityAndOffset(
message: string,
duration: number,
gravity: number,
xOffset: number,
yOffset: number
): void;
}// Accessibility-aware interactions
function AccessibleButton({onPress, children, accessibilityLabel, accessibilityHint}) {
return (
<TouchableOpacity
onPress={onPress}
accessible={true}
accessibilityRole="button"
accessibilityLabel={accessibilityLabel}
accessibilityHint={accessibilityHint}
accessibilityState={{disabled: false}}
>
{children}
</TouchableOpacity>
);
}
// Screen reader announcements
function AccessibilityAnnouncements() {
const announce = (message) => {
// Announce message to screen readers
AccessibilityInfo.announceForAccessibility(message);
};
const handleSave = () => {
// Perform save operation
saveData().then(() => {
announce('Data saved successfully');
}).catch(() => {
announce('Failed to save data');
});
};
return (
<Button
title="Save"
onPress={handleSave}
accessibilityLabel="Save data"
accessibilityHint="Saves your current progress"
/>
);
}
// Accessibility info detection
function AccessibilityInfo() {
const [isScreenReaderEnabled, setScreenReaderEnabled] = useState(false);
const [isReduceMotionEnabled, setReduceMotionEnabled] = useState(false);
useEffect(() => {
// Check screen reader status
AccessibilityInfo.isScreenReaderEnabled().then(setScreenReaderEnabled);
// Check reduce motion preference
AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotionEnabled);
// Listen for changes
const screenReaderSubscription = AccessibilityInfo.addEventListener(
'screenReaderChanged',
setScreenReaderEnabled
);
const reduceMotionSubscription = AccessibilityInfo.addEventListener(
'reduceMotionChanged',
setReduceMotionEnabled
);
return () => {
screenReaderSubscription.remove();
reduceMotionSubscription.remove();
};
}, []);
return (
<View>
<Text>Screen Reader: {isScreenReaderEnabled ? 'Enabled' : 'Disabled'}</Text>
<Text>Reduce Motion: {isReduceMotionEnabled ? 'Enabled' : 'Disabled'}</Text>
</View>
);
}// Long press interactions
function LongPressItem({item, onEdit, onDelete}) {
const showContextMenu = () => {
if (Platform.OS === 'ios') {
ActionSheetIOS.showActionSheetWithOptions(
{
options: ['Cancel', 'Edit', 'Delete'],
destructiveButtonIndex: 2,
cancelButtonIndex: 0,
},
(buttonIndex) => {
if (buttonIndex === 1) onEdit(item);
if (buttonIndex === 2) onDelete(item);
}
);
} else {
Alert.alert('Context Menu', 'Choose an action', [
{text: 'Cancel'},
{text: 'Edit', onPress: () => onEdit(item)},
{text: 'Delete', style: 'destructive', onPress: () => onDelete(item)},
]);
}
};
return (
<TouchableOpacity onLongPress={showContextMenu}>
<View style={styles.item}>
<Text>{item.title}</Text>
</View>
</TouchableOpacity>
);
}
// Haptic feedback with interactions
function HapticButton({onPress, children, feedbackType = 'impact'}) {
const handlePress = () => {
// Provide haptic feedback
if (Platform.OS === 'ios') {
// iOS haptic feedback would go here if available
Vibration.vibrate(50);
} else {
Vibration.vibrate(30);
}
onPress?.();
};
return (
<TouchableOpacity onPress={handlePress}>
{children}
</TouchableOpacity>
);
}
// Double tap detection
function DoubleTapView({onSingleTap, onDoubleTap, children}) {
const lastTap = useRef(null);
const handleTap = () => {
const now = Date.now();
const DOUBLE_PRESS_DELAY = 300;
if (lastTap.current && (now - lastTap.current) < DOUBLE_PRESS_DELAY) {
onDoubleTap?.();
} else {
setTimeout(() => {
if (lastTap.current && (Date.now() - lastTap.current) >= DOUBLE_PRESS_DELAY) {
onSingleTap?.();
}
}, DOUBLE_PRESS_DELAY);
}
lastTap.current = now;
};
return (
<TouchableWithoutFeedback onPress={handleTap}>
{children}
</TouchableWithoutFeedback>
);
}This comprehensive user interaction documentation provides developers with all the tools needed to create engaging, accessible, and platform-appropriate user experiences in React Native applications.
Install with Tessl CLI
npx tessl i tessl/npm-react-native@1000.0.0docs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10