ScaleRocket/Mobile

Haptics

Using expo-haptics for tactile feedback on button presses, success confirmations, and error alerts.

Haptics

Haptic feedback adds a physical dimension to your app's interactions. ScaleRocket Mobile uses expo-haptics to provide tactile responses for button presses, success/error states, and UI transitions.

Setup

npx expo install expo-haptics

Note: Haptics require a physical device. Simulators and emulators do not vibrate.

Basic Usage

import * as Haptics from "expo-haptics";

// Light tap — button press, toggle
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);

// Medium tap — selection change
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);

// Heavy tap — significant action
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);

Feedback Types

expo-haptics provides three categories of feedback:

Impact Feedback

Physical tap sensation with varying intensity:

// Light — subtle, for frequent interactions
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);

// Medium — moderate, for selections
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);

// Heavy — strong, for important actions
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);

Notification Feedback

Semantic feedback tied to outcomes:

// Success — action completed
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);

// Warning — caution needed
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);

// Error — action failed
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);

Selection Feedback

For picker wheels and selection changes:

Haptics.selectionAsync();

When to Use Haptics

InteractionHaptic TypeWhy
Button pressImpact.LightConfirm the tap registered
Toggle switchImpact.LightPhysical click feel
Delete actionImpact.HeavyEmphasize destructive action
Form submittedNotification.SuccessConfirm completion
Error shownNotification.ErrorAlert the user
Pull to refreshImpact.MediumThreshold reached
Picker scrollSelectionEach option change
Long pressImpact.HeavyContext menu trigger

Integration with Components

Haptic Button

Add haptics to the Button component:

import * as Haptics from "expo-haptics";

function HapticButton({ onPress, haptic = true, ...props }: ButtonProps) {
  const handlePress = () => {
    if (haptic) {
      Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
    }
    onPress?.();
  };

  return <Button {...props} onPress={handlePress} />;
}

Haptic Tab Bar

Add feedback when switching tabs:

// app/(tabs)/_layout.tsx
<Tabs
  screenListeners={{
    tabPress: () => {
      Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
    },
  }}
>
  {/* Tab screens */}
</Tabs>

Delete Confirmation

async function handleDelete() {
  Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);

  Alert.alert("Delete Item", "This cannot be undone.", [
    { text: "Cancel", style: "cancel" },
    {
      text: "Delete",
      style: "destructive",
      onPress: async () => {
        await deleteItem();
        Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
      },
    },
  ]);
}

Custom Hook

Create a reusable haptics hook:

import * as Haptics from "expo-haptics";
import { Platform } from "react-native";

export function useHaptics() {
  const isDevice = Platform.OS !== "web";

  return {
    light: () => isDevice && Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light),
    medium: () => isDevice && Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium),
    heavy: () => isDevice && Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy),
    success: () => isDevice && Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success),
    error: () => isDevice && Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error),
    warning: () => isDevice && Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning),
    selection: () => isDevice && Haptics.selectionAsync(),
  };
}

Usage:

function MyComponent() {
  const haptics = useHaptics();

  return (
    <Button title="Save" onPress={() => {
      haptics.light();
      handleSave();
    }} />
  );
}

Best Practices

  • Don't overdo it — Too much haptic feedback becomes annoying
  • Match intensity to importance — Light for routine, heavy for significant
  • Skip on web — Check platform before calling haptics
  • Respect user settings — iOS system haptics settings are respected automatically
  • Test on real devices — Simulators cannot produce haptic feedback

On this page