Morphing Blob Toasts for React Native – expo-gooey-toast

Description:

expo-gooey-toast is a React Native toast library that creates morphing pill-to-card notifications with gooey SVG transitions.

It’s built for Expo and React Native apps, uses Reanimated 4 and react-native-svg, and supports iOS and Android.

Features

  • Pill to card to pill toast transitions.
  • Animate toast UI with Reanimated 4 and SVG.
  • Show success, error, warning, info, and default states.
  • Track async work with loading, success, and error toast flows.
  • Support four body alignment patterns.
  • Add action buttons for quick follow-up work.
  • Show timestamps and small meta labels.
  • Dismiss toasts with swipe gestures.
  • Control toast lifetime with per-toast duration values.
  • Export TypeScript types for app-level integration.

Use Cases

  • Show save and sync feedback in an Expo form screen.
  • Report API request progress during async data fetches.
  • Surface validation and network errors in mobile checkout flows.
  • Display lightweight status messages in admin and dashboard apps.

See It In Action

How to Use It

1. Install & setup.

# Clone the repository that contains the toast source and demo app
git clone https://github.com/rit3zh/expo-gooey-toast
# Enter the project folder
cd expo-gooey-toast
# Install dependencies with your preferred package manager
bun install
# Start the Expo development server
bun start

2. The library exports GooeyToaster and gooeyToast from src/index.ts. Put the host near the top of your app tree so any screen can trigger a toast.

import React from "react";
import { View, Text, Pressable } from "react-native";
// Import the toast host and toast API from the project source
import { GooeyToaster, gooeyToast } from "@/src";
export default function App() {
  return (
    <View style={{ flex: 1 }}>
      {/* Your app UI lives here */}
      <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
        <Pressable
          // Trigger a basic toast from anywhere in the tree
          onPress={() => gooeyToast("Profile updated")}
        >
          <Text>Show toast</Text>
        </Pressable>
      </View>
      {/* Mount one toaster instance near the root */}
      <GooeyToaster position="top-center" visibleToasts={3} />
    </View>
  );
}

3. The public API includes the base gooeyToast() call plus typed helpers for success, error, warning, and info states.

import { gooeyToast } from "@/src";
// Show a neutral message
gooeyToast("Draft stored locally");
// Show a success message
gooeyToast.success("Upload complete");
// Show an info message
gooeyToast.info("New comment received");
// Show a warning message
gooeyToast.warning("Storage is almost full");
// Show an error message with extra body text
gooeyToast.error("Request failed", {
  // Add a secondary line for useful context
  description: "The server did not return a valid response.",
  // Move text and icon layout to the right side
  bodyLayout: "right",
});

4. gooeyToast.promise() is the most useful part of the API for real apps. It maps loading, success, and error states to a single promise flow. The README shows support for per-state descriptions too.

import { gooeyToast } from "@/src";
async function refreshAccount() {
  const request = fetch("https://example.com/api/account").then((res) => {
    if (!res.ok) {
      throw new Error("Bad response");
    }
    return res.json();
  });
  gooeyToast.promise(request, {
    // Text shown while the promise is still pending
    loading: "Refreshing account",
    // Text shown after the promise resolves
    success: "Account is current",
    // Text shown after the promise rejects
    error: "Refresh failed",
    // Per-state body text
    description: {
      loading: "Pulling the latest account details.",
      success: "The latest records are now on this device.",
      error: "Check your connection and try the request again.",
    },
  });
  return request;
}

5. Add action buttons and metadata:

import { gooeyToast } from "@/src";
gooeyToast.success("Invoice sent", {
  // Add a second line under the title
  description: "The customer copy is now in the outbox.",
  // Show a small metadata label
  meta: "billing",
  // Render a timestamp on the toast
  showTimestamp: true,
  // Attach a direct action button
  action: {
    label: "Undo",
    onClick: () => {
      // Roll back the last action
      console.log("Undo invoice send");
    },
  },
});

6. Dismiss one toast or clear them all:

import { gooeyToast } from "@/src";
// Keep the returned ID if your implementation exposes it
const toastId = gooeyToast("Connecting to workspace");
// Later, dismiss a single toast by ID
gooeyToast.dismiss(toastId);
// Clear every visible toast
gooeyToast.dismissAll();

7. All possible component props.

  • description (string): Adds a secondary text line under the title. ([GitHub][1])
  • bodyLayout (“left” | “center” | “right” | “spread”): Sets the body alignment pattern. Default: "left". ([GitHub][1])
  • duration (number): Sets the auto-dismiss time in milliseconds. Default: 4000. ([GitHub][1])
  • icon (ReactNode): Replaces the default icon area with custom content. ([GitHub][1])
  • action ({ label, onClick }): Adds one action button with a label and click handler. ([GitHub][1])
  • meta (string): Renders compact supporting text such as a source or status label. ([GitHub][1])
  • position (“top” | “bottom”): Sets the toast vertical direction for that toast. Default: "top". ([GitHub][1])
  • dismissible (boolean): Turns swipe or manual dismissal on or off. Default: true. ([GitHub][1])
  • showTimestamp (boolean): Shows a timestamp on the toast UI. Default: false. ([GitHub][1])

Alternatives