CtrlK
CommunityDocumentationLog inGet started
Tessl Logo

tessl/npm-react-native

A framework for building native apps using React

Overall
score

100%

Evaluation100%

1.06x

Agent success when using this tile

Overview
Eval results
Files

user-interaction.mddocs/

React Native User Interaction APIs

React Native provides comprehensive APIs for handling user interactions, displaying system dialogs, accessing device feedback capabilities, and platform-specific UI components.

Installation

npm install react-native

System Dialogs

Alert

Display 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';

Device Feedback APIs

Vibration

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;
}

Platform-Specific UI Components

ActionSheetIOS

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[];
}

ToastAndroid

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 and Interaction Helpers

Accessibility Features

// 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>
  );
}

Interaction Patterns

Long Press and Context Menus

// 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.0

docs

animation.md

core-components.md

index.md

native-bridge.md

platform-apis.md

react-hooks.md

styling.md

user-interaction.md

tile.json