Accessing Native Features
React Native provides access to native device features through built-in APIs and third-party libraries. Expo makes many features available out of the box, while React Native CLI requires installing native modules.
Camera Access
// Install: npx expo install expo-camera
import { CameraView, CameraType, useCameraPermissions } from 'expo-camera';
import { useState, useRef } from 'react';
function CameraScreen() {
const [facing, setFacing] = useState<CameraType>('back');
const [permission, requestPermission] = useCameraPermissions();
const cameraRef = useRef<CameraView>(null);
if (!permission) {
return <View />;
}
if (!permission.granted) {
return (
<View style={styles.container}>
<Text>We need camera permission</Text>
<Button title="Grant Permission" onPress={requestPermission} />
</View>
);
}
const takePicture = async () => {
if (cameraRef.current) {
const photo = await cameraRef.current.takePictureAsync();
console.log('Photo taken:', photo.uri);
}
};
const toggleCameraFacing = () => {
setFacing(current => current === 'back' ? 'front' : 'back');
};
return (
<View style={styles.container}>
<CameraView style={styles.camera} facing={facing} ref={cameraRef}>
<View style={styles.buttonContainer}>
<TouchableOpacity style={styles.button} onPress={toggleCameraFacing}>
<Text style={styles.text}>Flip</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.captureButton} onPress={takePicture}>
<View style={styles.captureInner} />
</TouchableOpacity>
</View>
</CameraView>
</View>
);
}
Location Services
// Install: npx expo install expo-location
import * as Location from 'expo-location';
import { useState, useEffect } from 'react';
function LocationScreen() {
const [location, setLocation] = useState<Location.LocationObject | null>(null);
const [address, setAddress] = useState<string | null>(null);
const [errorMsg, setErrorMsg] = useState<string | null>(null);
useEffect(() => {
(async () => {
// Request permission
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission denied');
return;
}
// Get current location
const location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
});
setLocation(location);
// Reverse geocode to get address
const [geocode] = await Location.reverseGeocodeAsync({
latitude: location.coords.latitude,
longitude: location.coords.longitude,
});
if (geocode) {
setAddress(`${geocode.street}, ${geocode.city}, ${geocode.country}`);
}
})();
}, []);
// Watch position changes
const startWatching = async () => {
const subscription = await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.High,
timeInterval: 1000,
distanceInterval: 10,
},
(newLocation) => {
setLocation(newLocation);
}
);
// Remember to remove subscription
return () => subscription.remove();
};
return (
<View style={styles.container}>
{location && (
<Text>
Lat: {location.coords.latitude.toFixed(4)}
Lng: {location.coords.longitude.toFixed(4)}
</Text>
)}
{address && <Text>Address: {address}</Text>}
</View>
);
}
Push Notifications
// Install: npx expo install expo-notifications expo-device
import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
import { useEffect, useRef, useState } from 'react';
// Configure notification behavior
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
function NotificationSetup() {
const [expoPushToken, setExpoPushToken] = useState('');
const notificationListener = useRef();
const responseListener = useRef();
useEffect(() => {
// Register for push notifications
registerForPushNotifications().then(token => {
setExpoPushToken(token);
// Send token to your server
});
// Listen for incoming notifications
notificationListener.current = Notifications.addNotificationReceivedListener(
notification => {
console.log('Notification received:', notification);
}
);
// Listen for user interaction with notification
responseListener.current = Notifications.addNotificationResponseReceivedListener(
response => {
const { notification } = response;
// Navigate to appropriate screen based on notification data
console.log('User tapped notification:', notification.request.content.data);
}
);
return () => {
Notifications.removeNotificationSubscription(notificationListener.current);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
return null;
}
async function registerForPushNotifications() {
if (!Device.isDevice) {
alert('Must use physical device for Push Notifications');
return;
}
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
return;
}
const token = await Notifications.getExpoPushTokenAsync({
projectId: 'your-project-id', // From app.json
});
return token.data;
}
// Schedule a local notification
async function scheduleNotification() {
await Notifications.scheduleNotificationAsync({
content: {
title: 'Reminder',
body: "Don't forget to check your tasks!",
data: { screen: 'Tasks' },
},
trigger: { seconds: 60 }, // In 1 minute
});
}
Image Picker
// Install: npx expo install expo-image-picker
import * as ImagePicker from 'expo-image-picker';
function ImagePickerScreen() {
const [image, setImage] = useState<string | null>(null);
const pickImage = async () => {
// Request permission
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions!');
return;
}
// Open image picker
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 0.8,
});
if (!result.canceled) {
setImage(result.assets[0].uri);
// Upload to server
await uploadImage(result.assets[0].uri);
}
};
const takePhoto = async () => {
const { status } = await ImagePicker.requestCameraPermissionsAsync();
if (status !== 'granted') {
alert('Sorry, we need camera permissions!');
return;
}
const result = await ImagePicker.launchCameraAsync({
allowsEditing: true,
aspect: [1, 1],
quality: 0.8,
});
if (!result.canceled) {
setImage(result.assets[0].uri);
}
};
return (
<View style={styles.container}>
<Button title="Pick from Gallery" onPress={pickImage} />
<Button title="Take Photo" onPress={takePhoto} />
{image && <Image source={{ uri: image }} style={styles.image} />}
</View>
);
}
Biometric Authentication
// Install: npx expo install expo-local-authentication
import * as LocalAuthentication from 'expo-local-authentication';
async function authenticateUser() {
// Check if hardware supports biometrics
const compatible = await LocalAuthentication.hasHardwareAsync();
if (!compatible) {
alert('Biometric authentication not available');
return false;
}
// Check what types are enrolled
const types = await LocalAuthentication.supportedAuthenticationTypesAsync();
// Returns: [1] for fingerprint, [2] for face recognition
// Check if biometrics are enrolled
const enrolled = await LocalAuthentication.isEnrolledAsync();
if (!enrolled) {
alert('No biometrics enrolled on this device');
return false;
}
// Authenticate
const result = await LocalAuthentication.authenticateAsync({
promptMessage: 'Authenticate to continue',
cancelLabel: 'Cancel',
fallbackLabel: 'Use passcode',
disableDeviceFallback: false,
});
if (result.success) {
console.log('Authentication successful!');
return true;
} else {
console.log('Authentication failed:', result.error);
return false;
}
}
// Usage
function SecureScreen() {
const [authenticated, setAuthenticated] = useState(false);
useEffect(() => {
authenticateUser().then(setAuthenticated);
}, []);
if (!authenticated) {
return <Text>Please authenticate to view this content</Text>;
}
return <Text>Secure content here</Text>;
}
Haptic Feedback
// Install: npx expo install expo-haptics
import * as Haptics from 'expo-haptics';
function HapticButton({ onPress, title }) {
const handlePress = () => {
// Light impact
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
onPress();
};
return <Button title={title} onPress={handlePress} />;
}
// Different haptic types
async function triggerHaptics() {
// Impact feedback (Light, Medium, Heavy)
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
// Notification feedback (Success, Warning, Error)
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
// Selection feedback (for selection changes)
await Haptics.selectionAsync();
}
Common Expo Modules Reference
| Feature | Package | Use Case |
|---|---|---|
| Camera | expo-camera | Take photos/videos |
| Location | expo-location | GPS, geocoding |
| Notifications | expo-notifications | Push & local notifications |
| File System | expo-file-system | Read/write files |
| Sharing | expo-sharing | Share content |
| Clipboard | expo-clipboard | Copy/paste |
| Sensors | expo-sensors | Accelerometer, gyro |
| Secure Store | expo-secure-store | Encrypted storage |