npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-sonner

v0.2.0

Published

An opinionated toast component for React Native.

Readme

React Native Sonner

An opinionated toast component for React Native inspired by Sonner.

Features

  • Minimalist design - Clean, modern toast notifications
  • Smooth animations - Spring and timing animations powered by Reanimated
  • Swipe to dismiss - Native gesture support with elastic resistance
  • Dark mode - Automatic theme detection
  • Cross-platform - iOS, Android, and Expo
  • Simple API - Just toast("Hello!") anywhere
  • Promise toasts - Loading, success, and error states
  • Rich colors - Optional vibrant backgrounds
  • Multi-toast support - Queue management with visible limit
  • Haptic feedback - Optional haptics for toast events
  • Accessibility - Screen reader announcements and labels
  • Performant - 60fps animations with worklet-based gestures
  • Highly customizable - Styles, icons, and animations

Installation

npm install react-native-sonner

Peer Dependencies

npm install react-native-reanimated react-native-gesture-handler react-native-safe-area-context

# Optional: for SVG icons
npm install react-native-svg

# Optional: for haptic feedback
npm install expo-haptics

Make sure to follow the installation instructions for each peer dependency:

Quick Start

1. Add the Toaster component

import { Toaster } from 'react-native-sonner';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context';

export default function App() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <SafeAreaProvider>
        <YourApp />
        <Toaster />
      </SafeAreaProvider>
    </GestureHandlerRootView>
  );
}

2. Show toasts anywhere

import { toast } from 'react-native-sonner';

function MyComponent() {
  return (
    <Button
      title="Show Toast"
      onPress={() => toast('Hello World!')}
    />
  );
}

API

Basic Usage

// Default toast
toast('Hello World!');

// With description
toast('Event created', {
  description: 'Your event has been scheduled',
});

Toast Variants

toast.success('Success!');
toast.error('Something went wrong');
toast.warning('Please check your input');
toast.info('New update available');
toast.loading('Uploading...');

Promise Toast

toast.promise(
  fetchData(),
  {
    loading: 'Loading data...',
    success: (data) => `Loaded ${data.count} items`,
    error: (err) => `Error: ${err.message}`,
  }
);

Update Toast

const toastId = toast.loading('Uploading...');

// Update the toast later
toast.update(toastId, {
  type: 'success',
  title: 'Upload complete!',
});

With Actions

toast('File deleted', {
  action: {
    label: 'Undo',
    onClick: () => restoreFile(),
  },
  cancel: {
    label: 'Dismiss',
    onClick: () => {},
  },
});

Custom Duration

// 10 seconds
toast('Long toast', { duration: 10000 });

// Never auto-dismiss
toast('Sticky toast', { duration: Infinity });

Important Toasts

Important toasts won't be hidden when the visible toast limit is reached:

toast.error('Critical error!', { important: true });

Dismiss Programmatically

const toastId = toast('Loading...');

// Dismiss specific toast
toast.dismiss(toastId);

// Dismiss all toasts
toast.dismiss();

Callbacks

toast('Hello', {
  onDismiss: (t) => console.log('Toast dismissed:', t.id),
  onAutoClose: (t) => console.log('Toast auto-closed:', t.id),
});

Toaster Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | position | Position | "top-center" | Toast position on screen | | theme | "light" \| "dark" \| "system" | "system" | Color theme | | duration | number | 4000 | Default duration in ms | | visibleToasts | number | 3 | Max visible toasts | | gap | number | 12 | Gap between toasts | | offset | object | { top: 52, bottom: 52, left: 16, right: 16 } | Screen edge offsets | | swipeToDismiss | boolean | true | Enable swipe gestures | | swipeDirection | SwipeDirection \| SwipeDirection[] | ["left", "right"] | Swipe directions | | pauseOnAppBackground | boolean | true | Pause timer when app backgrounds | | richColors | boolean | false | Vibrant backgrounds | | closeButton | boolean | false | Show close button | | hapticFeedback | boolean | false | Enable haptic feedback | | icons | ToastIcons | - | Custom icons | | toastStyles | ToastStyles | - | Default styles for all toasts | | variantStyles | VariantStyles | - | Per-variant style overrides | | containerStyle | ViewStyle | - | Container style | | animation | AnimationConfig | - | Animation configuration | | toasterId | string | - | ID for multiple Toaster instances |

Position Options

  • "top-left" / "top-center" / "top-right"
  • "bottom-left" / "bottom-center" / "bottom-right"

Animation Configuration

<Toaster
  animation={{
    duration: 350,      // Entry animation duration (ms)
    exitDuration: 200,  // Exit animation duration (ms)
    useSpring: true,    // Use spring animation for entry
    damping: 18,        // Spring damping
    stiffness: 140,     // Spring stiffness
    mass: 1,            // Spring mass
  }}
/>

Per-toast animation:

toast('Custom animation', {
  animation: {
    duration: 500,
    useSpring: false,
  },
});

Haptic Feedback

Enable haptic feedback for toast events (requires expo-haptics):

<Toaster hapticFeedback />

Haptics are triggered on:

  • Toast appearance (light impact, or notification type for success/warning/error)
  • Action button press (medium impact)
  • Cancel button press (light impact)

Accessibility

Toasts automatically announce to screen readers. Customize accessibility:

toast('Order placed', {
  accessibility: {
    announceToScreenReader: true,  // default: true
    accessibilityLabel: 'Your order has been successfully placed',
  },
});

Styling

Custom Toast Styles

toast('Styled toast', {
  styles: {
    container: { backgroundColor: '#1a1a1a' },
    title: { color: '#fff', fontWeight: 'bold' },
    description: { color: '#888' },
  },
});

Global Styles via Toaster

<Toaster
  toastStyles={{
    container: { borderRadius: 20 },
    title: { fontFamily: 'Inter-Medium' },
  }}
/>

Per-Variant Styles

Style specific toast variants differently:

<Toaster
  variantStyles={{
    success: {
      container: { borderLeftWidth: 4, borderLeftColor: '#22c55e' },
      title: { fontWeight: '600' },
    },
    error: {
      container: { borderLeftWidth: 4, borderLeftColor: '#ef4444' },
    },
    warning: {
      container: { backgroundColor: '#fef3c7' },
    },
    info: {
      title: { color: '#1e40af' },
    },
    // 'loading' and 'default' variants also available
  }}
/>

Style priority (lowest to highest):

  1. Base styles (theme defaults)
  2. toastStyles (applies to all toasts)
  3. variantStyles[type] (per-variant overrides)
  4. toast.styles (individual toast override)

Available Style Keys

  • container - Toast container
  • content - Content wrapper
  • title - Title text
  • description - Description text
  • actionButton - Action button
  • actionButtonText - Action button text
  • cancelButton - Cancel button
  • cancelButtonText - Cancel button text
  • closeButton - Close button

Custom Icons

<Toaster
  icons={{
    success: <MySuccessIcon />,
    error: <MyErrorIcon />,
    warning: <MyWarningIcon />,
    info: <MyInfoIcon />,
    loading: <MySpinner />,
  }}
/>

Per-toast icon:

toast('Custom icon', {
  icon: <MyIcon />,
});

Multiple Toaster Instances

Use toasterId to target specific Toaster instances:

// In your layout
<Toaster toasterId="main" position="top-center" />
<Toaster toasterId="bottom" position="bottom-center" />

// Show toast in specific toaster
toast('Top notification', { toasterId: 'main' });
toast('Bottom notification', { toasterId: 'bottom' });

Hooks

useToastState

Access toast state in components:

import { useToastState } from 'react-native-sonner';

function ToastCounter() {
  const { toasts } = useToastState();
  return <Text>Active toasts: {toasts.length}</Text>;
}

Expo Support

React Native Sonner works with Expo out of the box:

npx expo install react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-svg expo-haptics

TypeScript

Full TypeScript support with exported types:

import type {
  ToastT,
  ToastType,
  Position,
  ToasterProps,
  AnimationConfig,
  ToastStyles,
  ToastIcons,
  VariantStyles,
} from 'react-native-sonner';

License

MIT