ScaleRocket/Mobile

Toast

Toast and alert notifications on mobile using Alert.alert() and custom toast components.

Toast

Mobile apps need feedback for user actions. ScaleRocket Mobile supports both native alerts and custom toast notifications.

Native Alert

The simplest approach is React Native's built-in Alert.alert():

import { Alert } from "react-native";

// Simple alert
Alert.alert("Success", "Your profile has been updated.");

// Alert with actions
Alert.alert(
  "Delete Account",
  "Are you sure? This action cannot be undone.",
  [
    { text: "Cancel", style: "cancel" },
    { text: "Delete", style: "destructive", onPress: handleDelete },
  ]
);
PlatformBehavior
iOSNative modal dialog
AndroidMaterial dialog

When to Use Alert vs Toast

ScenarioUse
Destructive confirmationAlert.alert() with actions
Success feedbackToast
Error messageToast (non-blocking) or Alert (critical)
Network errorToast with retry action
Form validationInline errors (not toast)

Custom Toast Component

For non-blocking notifications, use a custom toast:

import { useRef } from "react";
import { Animated, Text, StyleSheet } from "react-native";

interface ToastProps {
  message: string;
  type?: "success" | "error" | "info";
}

function Toast({ message, type = "info" }: ToastProps) {
  const opacity = useRef(new Animated.Value(0)).current;

  const show = () => {
    Animated.sequence([
      Animated.timing(opacity, {
        toValue: 1,
        duration: 300,
        useNativeDriver: true,
      }),
      Animated.delay(2000),
      Animated.timing(opacity, {
        toValue: 0,
        duration: 300,
        useNativeDriver: true,
      }),
    ]).start();
  };

  const colors = {
    success: "#10B981",
    error: "#EF4444",
    info: "#3B82F6",
  };

  return (
    <Animated.View
      style={[styles.toast, { opacity, backgroundColor: colors[type] }]}
    >
      <Text style={styles.text}>{message}</Text>
    </Animated.View>
  );
}

const styles = StyleSheet.create({
  toast: {
    position: "absolute",
    top: 60,
    left: 16,
    right: 16,
    padding: 16,
    borderRadius: 12,
    zIndex: 1000,
  },
  text: { color: "white", fontWeight: "600", textAlign: "center" },
});

Toast Context

Create a context so any screen can trigger a toast:

import { createContext, useContext, useState, useCallback } from "react";

const ToastContext = createContext<{
  show: (message: string, type?: "success" | "error" | "info") => void;
} | null>(null);

export function ToastProvider({ children }: { children: React.ReactNode }) {
  const [toast, setToast] = useState<ToastProps | null>(null);

  const show = useCallback((message: string, type: "success" | "error" | "info" = "info") => {
    setToast({ message, type });
    setTimeout(() => setToast(null), 3000);
  }, []);

  return (
    <ToastContext.Provider value={{ show }}>
      {children}
      {toast && <Toast message={toast.message} type={toast.type} />}
    </ToastContext.Provider>
  );
}

export const useToast = () => useContext(ToastContext)!;

Usage from any screen:

function ProfileScreen() {
  const toast = useToast();

  const handleSave = async () => {
    try {
      await saveProfile();
      toast.show("Profile saved!", "success");
    } catch {
      toast.show("Failed to save profile", "error");
    }
  };

  return <Button title="Save" onPress={handleSave} />;
}

Setup in Root Layout

Wrap your app with ToastProvider:

// app/_layout.tsx
import { ToastProvider } from "@/components/ui/Toast";

export default function RootLayout() {
  return (
    <ToastProvider>
      <Slot />
    </ToastProvider>
  );
}

Toast with Action

Add an undo or retry button:

<Animated.View style={[styles.toast, { opacity }]}>
  <Text style={styles.text}>{message}</Text>
  {action && (
    <TouchableOpacity onPress={action.onPress}>
      <Text style={styles.action}>{action.label}</Text>
    </TouchableOpacity>
  )}
</Animated.View>

On this page