Toast
Notification toast with slide-in animation and auto-dismiss.
import { ToastProvider, useToast } from "@/components/ui/toast";
// Wrap your app with ToastProvider
export function App() {
return (
<ToastProvider>
<MyScreen />
</ToastProvider>
);
}
function MyScreen() {
const { toast } = useToast();
return (
<Button
onPress={() =>
toast({ title: "Success!", description: "Your action was completed." })
}
>
Show Toast
</Button>
);
}Installation
npx @aniui/cli add toastThis component requires react-native-reanimated for slide and fade animations.
Usage
app/index.tsx
import { ToastProvider, useToast } from "@/components/ui/toast";
// Wrap your app with ToastProvider
export function App() {
return (
<ToastProvider>
<MyScreen />
</ToastProvider>
);
}
function MyScreen() {
const { toast } = useToast();
return (
<Button
onPress={() =>
toast({ title: "Success!", description: "Your action was completed." })
}
>
Show Toast
</Button>
);
}Variants
app/index.tsx
// Default toast
toast({ title: "Notification", description: "Something happened." });
// Destructive toast
toast({ title: "Error", description: "Something went wrong.", variant: "destructive" });
// Success toast
toast({ title: "Saved", description: "Changes saved successfully.", variant: "success" });Props
useToast
Returns an object with a toast function.
PropTypeDefault
titlestringrequireddescriptionstring—variant"default" | "destructive" | "success""default"ToastProvider
PropTypeDefault
childrenReact.ReactNoderequiredSource
components/ui/toast.tsx
import React, { createContext, useCallback, useContext, useState } from "react";
import { View, Text, Pressable } from "react-native";
import Animated, { SlideInUp, SlideOutUp, FadeOut } from "react-native-reanimated";
import { cn } from "@/lib/utils";
type ToastVariant = "default" | "destructive" | "success";
type ToastData = { id: string; title: string; description?: string; variant?: ToastVariant };
const ToastContext = createContext<{
toast: (data: Omit<ToastData, "id">) => void;
}>({ toast: () => {} });
export function useToast() {
return useContext(ToastContext);
}
export interface ToastProviderProps {
children: React.ReactNode;
}
export function ToastProvider({ children }: ToastProviderProps) {
const [toasts, setToasts] = useState<ToastData[]>([]);
const toast = useCallback((data: Omit<ToastData, "id">) => {
const id = Date.now().toString();
setToasts((prev) => [...prev, { ...data, id }]);
setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), 3000);
}, []);
const dismiss = (id: string) => setToasts((prev) => prev.filter((t) => t.id !== id));
return (
<ToastContext.Provider value={{ toast }}>
{children}
<View className="absolute top-14 left-4 right-4 gap-2 z-50" pointerEvents="box-none">
{toasts.map((t) => (
<ToastItem key={t.id} data={t} onDismiss={() => dismiss(t.id)} />
))}
</View>
</ToastContext.Provider>
);
}
const variantStyles: Record<ToastVariant, string> = {
default: "bg-card border-border",
destructive: "bg-destructive border-destructive",
success: "bg-green-600 border-green-600",
};
function ToastItem({ data, onDismiss }: { data: ToastData; onDismiss: () => void }) {
const variant = data.variant ?? "default";
const isDefault = variant === "default";
return (
<Animated.View entering={SlideInUp.duration(300)} exiting={SlideOutUp.merge(FadeOut).duration(200)}>
<Pressable
className={cn("rounded-lg border p-4 shadow-lg", variantStyles[variant])}
onPress={onDismiss}
accessible={true}
accessibilityRole="alert"
>
<Text className={cn("text-sm font-semibold", isDefault ? "text-foreground" : "text-white")}>
{data.title}
</Text>
{data.description && (
<Text className={cn("text-xs mt-1", isDefault ? "text-muted-foreground" : "text-white/80")}>
{data.description}
</Text>
)}
</Pressable>
</Animated.View>
);
}