Shipnative
ShipnativeShipnative
Sign in
GuideSeptember 2026 · 8 min read

React Native Toast: Which Library, and How (2026)

A toast is the smallest useful piece of UI in an app and one of the easiest to get subtly wrong. It fires and nobody sees it. It slides under the notch. It disappears the instant the user navigates — which is precisely when you wanted to say “saved.” This is the working reference: the four real options in 2026, setup code you can paste, and the three structural reasons a toast fires without ever reaching a human eye.

React Native has no built-in toast

Worth saying plainly, because it surprises people coming from Android: React Native core ships ToastAndroidand nothing else. On iOS there is no system toast at all — what you see in iOS apps is either a native banner (the same presentation style as a notification) or a view the app draws itself. So “add a toast” is always a decision about which of those two you want.

The split matters more than the library name. A JS view lives inside your React tree: fully stylable, themeable, testable — and coverable by anything that renders above it. A native toast is drawn by the OS in its own window: it can never be covered, and you can barely style it.

The four options, honestly

OptionTypePlatformsPick it forThe cost
react-native-toast-messageJS viewiOS · Android · WebFull control over the toast component, imperative API from anywhereYou style everything and handle safe area yourself
sonner-nativeJS viewiOS · AndroidStacking, swipe-to-dismiss, and promise toasts out of the boxRequires Reanimated and gesture-handler; opinionated look
burntNativeiOS · AndroidReal native iOS banner and Android toast — feels like the OS, not your appAlmost no styling control; needs a dev build, not Expo Go
ToastAndroid (core)NativeAndroid onlyZero dependencies, impossible to cover with your own viewsSilently does nothing on iOS
DIY + ReanimatedJS viewiOS · Android · WebNo dependency, exactly your design systemYou own queueing, timers, gestures, and accessibility

If you have no strong opinion, use react-native-toast-message. It is pure JavaScript, so it survives Expo Go and React Native Web, the imperative API can be called from a network layer that has no access to React context, and the toast body is your own component — which means it inherits your design tokens instead of fighting them.

The working setup

Two files. A config that defines what a toast looks like, and one mount at the very root of the app — after the navigator, never inside a screen.

// toast-config.tsx
import { Text, View, StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

function Base({ text1, text2, accent }) {
  // The toast host is NOT inside your SafeAreaView, so it inherits
  // no inset. Read it here or the notch eats the first line.
  const insets = useSafeAreaInsets();

  return (
    <View style={[styles.card, { marginTop: insets.top + 8, borderLeftColor: accent }]}>
      <Text style={styles.title} numberOfLines={1}>{text1}</Text>
      {text2 ? <Text style={styles.body} numberOfLines={2}>{text2}</Text> : null}
    </View>
  );
}

export const toastConfig = {
  success: (props) => <Base {...props} accent="#22c55e" />,
  error:   (props) => <Base {...props} accent="#ef4444" />,
  info:    (props) => <Base {...props} accent="#f97316" />,
};

const styles = StyleSheet.create({
  card: {
    width: '92%',
    borderRadius: 12,
    borderLeftWidth: 4,
    paddingVertical: 12,
    paddingHorizontal: 14,
    backgroundColor: '#242424',
    // Android needs elevation, iOS needs shadow* — set both.
    elevation: 6,
    shadowColor: '#000',
    shadowOpacity: 0.3,
    shadowRadius: 12,
    shadowOffset: { width: 0, height: 4 },
  },
  title: { color: '#fff', fontSize: 15, fontWeight: '600' },
  body:  { color: 'rgba(255,255,255,0.7)', fontSize: 13, marginTop: 2 },
});
// app/_layout.tsx  (Expo Router)
import { Stack } from 'expo-router';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import Toast from 'react-native-toast-message';
import { toastConfig } from '../toast-config';

export default function RootLayout() {
  return (
    <SafeAreaProvider>
      <Stack />
      {/* Last child of the root. Mounted once, outliving every screen. */}
      <Toast config={toastConfig} topOffset={0} />
    </SafeAreaProvider>
  );
}

Now anything — a screen, a mutation hook, an interceptor in your API client — can call it without props or context:

import Toast from 'react-native-toast-message';

Toast.show({ type: 'success', text1: 'Saved', visibilityTime: 2000 });

Toast.show({
  type: 'error',
  text1: 'Could not sync',
  text2: 'We will retry when you are back online.',
  visibilityTime: 4000,
});

Three reasons your toast fires but nobody sees it

These are the bugs that eat an afternoon, and none of them throw an error.

  1. The host is mounted inside a screen. If <Toast /> lives in a screen component, navigating away unmounts it mid-animation. The classic symptom is that the toast works when you stay put and silently fails on the one flow that matters — save-then-go-back. Mount it once, at the root, as the last child.
  2. A Modal is above it. React Native’s Modal is not a view in your tree — it opens a separate native window that sits above the entire React root, toast host included. Nothing you do with zIndex will fix it. Either avoid firing toasts while a Modal is open, switch that Modal to a router modal route or a bottom sheet (both of which stay inside your tree), or fall back to a native toast for that one case. The modal guide covers which kind you actually want.
  3. It fired during a transition. Calling Toast.show() in the same tick as a navigation.goBack() races the screen animation, and on Android the entrance animation often gets dropped. Fire it from the destination instead — or defer with InteractionManager.runAfterInteractions(() => Toast.show(...)), which waits for the transition to settle.

Rolling your own with Reanimated

Worth doing when your design system is strict and you only need one toast at a time. The whole thing is a shared value, a timer, and an absolutely-positioned view:

import { createContext, useCallback, useContext, useRef, useState } from 'react';
import { AccessibilityInfo, StyleSheet, Text } from 'react-native';
import Animated, { FadeInUp, FadeOutUp } from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

const ToastContext = createContext((msg: string) => {});
export const useToast = () => useContext(ToastContext);

export function ToastProvider({ children }) {
  const [message, setMessage] = useState<string | null>(null);
  const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const insets = useSafeAreaInsets();

  const show = useCallback((msg: string) => {
    // Replace, do not queue — a queue of stale toasts is worse than none.
    if (timer.current) clearTimeout(timer.current);
    setMessage(msg);
    // Screen readers do not announce a view appearing. Say it out loud.
    AccessibilityInfo.announceForAccessibility(msg);
    timer.current = setTimeout(() => setMessage(null), 2600);
  }, []);

  return (
    <ToastContext.Provider value={show}>
      {children}
      {message && (
        <Animated.View
          entering={FadeInUp.duration(180)}
          exiting={FadeOutUp.duration(140)}
          pointerEvents="none"
          accessibilityLiveRegion="polite"
          style={[styles.toast, { top: insets.top + 12 }]}
        >
          <Text style={styles.text}>{message}</Text>
        </Animated.View>
      )}
    </ToastContext.Provider>
  );
}

const styles = StyleSheet.create({
  toast: {
    position: 'absolute',
    left: 16,
    right: 16,
    padding: 14,
    borderRadius: 12,
    backgroundColor: '#242424',
    elevation: 6,
  },
  text: { color: '#fff', fontSize: 15 },
});

Two details that separate this from the version most people ship. pointerEvents="none" stops the toast swallowing taps on whatever it floats over — without it, a toast over a list makes the top row unclickable for three seconds. And announceForAccessibility matters because VoiceOver and TalkBack do not narrate a view that simply appears; a purely visual toast is invisible to a screen-reader user, which is exactly the audience least able to guess that the save worked.

When not to use a toast

The failure mode of a good toast component is that it becomes the answer to everything. A rule that holds up: a toast is for something the user is allowed to miss.

  • Form validation belongs under the field that failed, not in a banner that vanishes before the user finishes reading it.
  • Destructive confirmation belongs in an Alert — a toast cannot be acknowledged.
  • Anything with an action(“Undo”) needs a longer timeout and pointerEvents="auto", and it needs to survive a navigation. If you find yourself building that, you want a snackbar, not a toast.
  • Offline and sync state is a persistent condition, not an event. Use a bar that stays until the condition clears.

Skip the wiring

Toast host at the root, safe-area insets, an accessible announcement, a themed toast body — it is maybe forty lines, and it is forty lines you rewrite in every project. ShipNative generates a real React Native app from a description with this scaffolding already in place, previews it on your phone, and hands you the full Expo project to keep. Describe the app in a sentence and edit the toast component like any other file you wrote.

Frequently Asked Questions

What is the best toast library for React Native in 2026?

react-native-toast-message is still the safe default: it is pure JS, works on iOS, Android and React Native Web, and gives you full control over the toast component. Pick sonner-native if you want stacking and swipe-to-dismiss for free, and burnt if you specifically want the real native iOS banner rather than a JS view that imitates one.

Why is my React Native toast not showing?

Almost always one of three things. The toast host is mounted inside a screen instead of at the app root, so it unmounts the moment you navigate. The toast is rendering behind a Modal, because a React Native Modal creates its own native window that sits above everything in your React tree. Or you called the toast during a screen transition and the animation was cancelled. Mount the host once at the root, render it after your navigator, and fire the toast after the transition settles.

Can I use ToastAndroid in React Native?

Yes, but only on Android — ToastAndroid is a thin wrapper over the platform Toast API and does nothing on iOS. It is genuinely useful for throwaway debug messages and for Android-only apps, because it costs zero dependencies and cannot be covered by your own view hierarchy. For anything cross-platform or branded, use a real toast library.

How do I stop a toast from being cut off by the notch?

Add the top safe-area inset to the toast container yourself. Toast libraries render into a plain absolutely-positioned view, not inside your SafeAreaView, so they do not inherit any inset. Read useSafeAreaInsets() inside your custom toast component and add insets.top to its padding — do not wrap the toast host in a SafeAreaView, which will also shrink the area your toast can animate through.

Should a toast or an alert be used for errors?

A toast is for information the user can ignore: saved, copied, sent, undo available. An error the user must act on — a failed payment, a destructive confirmation, a permission the app cannot work without — belongs in an Alert or an inline message next to the control that failed. A toast that disappears after three seconds is a bad place to put something the user needs to read.

→

React Native Modal: Which One to Use

Why a Modal sits above your toast host — and what to do about it.

Read guide →
→

React Native Animations with Reanimated

The shared-value patterns behind a toast you build yourself.

Read guide →

Ship a real React Native app today

Describe, preview, and export Expo code — free to start.

Build with ShipNative →
ShipNative logoShipnative

Build mobile apps with AI. Describe, preview, and ship to iOS & Android in minutes.

Features

Text to App AIApp Generator from ScreenshotPRD to Mobile App

Tools

All free toolsApp Cost CalculatorApp Name GeneratorApp Store Keyword ToolReact Native Components

Blog

All blog postsHow to Build an App Without CodingBest AI Tools for Real Mobile AppsExpo EAS App Store ChecklistLovable, Cursor & v0 for MobileBest AI App Builders in 2026React Native AI App Builder Guide

Legal

FAQTerms of ServicePrivacy Policy

© 2026 ShipNative. All rights reserved.