React Native Dynamic Island Alerts – Pretty Toast

Description:

react-native-pretty-toast is a toast notification library that morphs directly out of the hardware cutout on iOS and Android.

On iPhone 14 Pro and later, the pill expands from the Dynamic Island. On Android, it anchors to the DisplayCutout hole-punch camera position, read automatically from system insets. Devices with no cutout receive a slide-in fallback.

The library exposes a single shared API across iOS, Android, and Web, and it requires React Native’s New Architecture (Fabric).

More Features

  • Backdrop-aware outline samples the content underneath and flips between accent and neutral stroke colors for legibility on any background.
  • Exposes an imperative API callable from redux middleware, fetch interceptors, and utility modules.
  • 5 toast variants: success, error, info, warning, and loading states.
  • Promise-based toast lifecycle morphs automatically from loading to success or error on promise settlement.
  • FIFO queue with a configurable depth cap and a force-preempt option for critical notifications.
  • Swipe-up to dismiss and a tappable trailing action button for undo or retry flows.
  • Fires separate lifecycle hooks on show, hide, and auto-dismiss events.
  • Screen-reader announcements on native via AccessibilityInfo and on web via aria-live regions.

See It In Action

How to Use It

Installation

react-native-pretty-toast requires React Native 0.76 or later with the New Architecture enabled. It does not support the Old Architecture or the Paper renderer.

npm install react-native-pretty-toast

iOS Setup

Add the following key to your app’s Info.plist:

<!-- Required: lets the toast control status bar visibility during presentation -->
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>

If you use Expo, add this to your app.json:

{
  "expo": {
    "ios": {
      "infoPlist": {
        "UIViewControllerBasedStatusBarAppearance": true
      }
    }
  }
}

iOS 15.1 is the minimum supported version. Dynamic Island behavior requires iPhone 14 Pro or later. Older devices receive the slide-in fallback automatically.

Provider Setup

Wrap your root component with ToastProvider once:

import { ToastProvider } from 'react-native-pretty-toast';
export default function App() {
  return (
    // Mount once at the root — all child components share this provider
    <ToastProvider maxQueue={3} defaultConfig={{ duration: 4000 }}>
      <RootNavigator />
    </ToastProvider>
  );
}

Triggering Toasts

The library offers two entry points. Pick whichever fits your code structure.

Imperative API (works anywhere)

import { toast } from 'react-native-pretty-toast';
// Call from a redux action, fetch interceptor, or any non-component module
toast.show({
  icon: 'checkmark.seal.fill',
  title: 'Profile Updated',
  message: 'Your changes have been saved.',
});

useToast hook (inside components)

import { useToast } from 'react-native-pretty-toast';
function ProfileScreen() {
  const toast = useToast();
  return (
    <Button
      title="Save Profile"
      onPress={() =>
        toast.show({
          icon: 'checkmark.seal.fill',
          title: 'Profile Updated',
          message: 'Your changes have been saved.',
          duration: 3000, // auto-dismiss after 3 seconds
        })
      }
    />
  );
}

Both the imperative toast object and the useToast hook share the same internal queue. Calls from either entry point behave identically.

<ToastProvider> Props

  • children (ReactNode): Your app tree.
  • useDynamicIsland (boolean): Set to false to always use the slide-in variant, even on Dynamic Island devices. Defaults to true.
  • defaultConfig (ToastProviderDefaults): Default values merged into every toast. Per-toast values always win. Excludes content fields (id, title, message, icon, iconSource, onPress, action).
  • maxQueue (number): Maximum queue depth, excluding the currently visible toast. When exceeded, the oldest queued toast is dropped. Set to 0 to disable queueing. Defaults to unlimited.

toast.show() Configuration Options

  • id (string): Stable identifier. Auto-generated when omitted.
  • icon (SFSymbolName): SF Symbol name (e.g. 'checkmark.seal.fill'). On Android, mapped to a bundled drawable via substring match. On web, mapped to a unicode glyph. The library ignores icon when iconSource is set.
  • iconSource (ImageSourcePropType): Custom image source — require(...), remote URL, or file URI. Overrides icon when set.
  • title (string): Bold title line.
  • message (string): Subtitle line. The pill expands in height to fit the content.
  • duration (number): Auto-dismiss delay in milliseconds. Defaults to 3000. The library ignores this when autoDismiss is false or duration <= 0.
  • autoDismiss (boolean): Set to false to keep the toast visible until explicitly dismissed. Defaults to true.
  • enableSwipeDismiss (boolean): Swipe-up to dismiss. Defaults to true.
  • accentColor (ColorValue): Overrides the icon-derived tint color. Controls both the icon tint and the pill accent stroke.
  • strokeColor (ColorValue): Fixed stroke color. Pass rgba(...) for transparent strokes. Overrides backdrop sampling when set.
  • disableBackdropSampling (boolean): Skips the backdrop luminance sampler that flips the outline between accent and neutral. Defaults to false.
  • action (ToastAction): Trailing button with { label, onPress } shape. Tapping it dismisses the toast.
  • accessibilityAnnouncement (string): Custom screen-reader announcement text. Defaults to title + message. An empty string silences the announcement.
  • onPress (() => void): Called when the user taps the pill body, not the action button. Dismisses the toast after.
  • onShow (() => void): Fires when the toast begins presenting.
  • onHide (() => void): Fires when the toast finishes dismissing, for any reason.
  • onAutoDismiss (() => void): Fires only when dismissal was triggered by the duration timer. Fires before onHide.

ShowOptions

  • force (boolean): Preempts the currently visible toast and presents the new one immediately.

API Methods

// Show a success toast — pre-fills a checkmark icon
toast.success('Payment Received');
// Show an error toast with an extended message and custom duration
toast.error('Upload Failed', {
  message: 'Your connection dropped. Please try again.',
  duration: 5000,
});
// Show an info toast
toast.info('New Version Available');
// Show a warning toast
toast.warning('Storage Almost Full');
// Show a persistent loading toast — autoDismiss defaults to false
const loadingId = toast.loading('Processing your request…');
// Update a live or queued toast in place — no re-enter animation on live toasts
toast.update(loadingId, {
  title: 'Request Complete',
  icon: 'checkmark.circle.fill',
  autoDismiss: true,
});
// Tie a toast lifecycle to a promise
// Returns the original promise so await chains are preserved
await toast.promise(api.uploadFile(file), {
  loading: 'Uploading file…',
  success: (result) => `Uploaded: ${result.filename}`,
  error: (e) => `Upload failed: ${(e as Error).message}`,
});
// Dismiss the currently visible toast, or a queued toast by id
toast.dismiss(loadingId);
// Dismiss the visible toast and clear the entire queue
toast.dismissAll();
// Preempt the currently visible toast with a critical alert
toast.show({ title: 'Authentication Required' }, { force: true });

SF Symbol Icon Color Mapping

The icon prop accepts any SF Symbol name. The tint color derives automatically from the symbol name:

Symbol containsColor
checkmarkGreen
xmarkRed
exclamationOrange
infoBlue
heartPink
arrowBlue
OtherGray

Pass accentColor to override the derived tint for any symbol. Browse the full SF Symbol catalog at sfsymbols.com or in Apple’s SF Symbols desktop app.

Advanced Examples

// Persistent toast with an undo action — stays until the user acts
toast.show({
  icon: 'trash.fill',
  title: 'Draft Deleted',
  autoDismiss: false,
  action: {
    label: 'Undo',
    onPress: () => restoreDraft(),
  },
});
// Custom remote avatar as the toast icon
toast.show({
  iconSource: { uri: 'https://cdn.example.com/user-avatar.png' },
  title: 'New Message from Sarah',
  message: 'Are you free this afternoon?',
});
// Analytics lifecycle hooks
toast.show({
  title: 'Sync Complete',
  onShow: () => analytics.track('toast_displayed'),
  onAutoDismiss: () => analytics.track('toast_timed_out'),
  onHide: () => runCleanup(),
});

Alternatives

FAQs

Q: Does this library work with Expo?
A: Yes. Add the UIViewControllerBasedStatusBarAppearance key to the ios.infoPlist field in your app.json.

Q: Can I call toast.show() outside a React component?
A: Yes. The imperative toast object writes to a singleton store. You can call it from redux middleware, a fetch interceptor, or any utility module. The ToastProvider must be mounted first.

Q: How do I keep a loading toast visible until an async operation finishes?
A: Use toast.loading(), which sets autoDismiss: false by default. Store the returned id and call toast.update(id, { title: 'Done', autoDismiss: true }) when the operation completes. toast.promise() handles this pattern automatically.

Q: The status bar renders on top of the toast on iOS. How do I fix this?
A: Add UIViewControllerBasedStatusBarAppearance set to true in your Info.plist. This key is required for the library to control status bar visibility during toast presentation.

Tags: