Himesh Dua

Component Preview

Click to trigger toast

'use client';

import {cn} from '@/lib/utils';
import {motion, AnimatePresence} from 'framer-motion';
import {useEffect, useState} from 'react';

type ToastProps = {
  message: string;
  once?: boolean;
  open?: boolean;
  duration?: number;
  onClose?: () => void;
  dr: 'br' | 'b' | 'bl' | 'tr' | 't' | 'tl';
};

const positionStyles: Record<ToastProps['dr'], {container: string; motion: any}> = {
  br: {
    container: 'bottom-6 right-6',
    motion: {opacity: 1},
  },
  bl: {
    container: 'bottom-6 left-6',
    motion: {opacity: 1},
  },
  b: {
    container: 'bottom-6 left-1/2 -translate-x-1/2',
    motion: {opacity: 1},
  },
  tr: {
    container: 'top-6 right-6',
    motion: {opacity: 1},
  },
  tl: {
    container: 'top-6 left-6',
    motion: {opacity: 1},
  },
  t: {
    container: 'top-6 left-1/2 -translate-x-1/2',
    motion: {opacity: 1},
  },
};

export function Toast({message, open, once, duration = 3000, onClose, dr = 'b'}: ToastProps) {
  const pos = positionStyles[dr];
  const [hasShownRef, setHasShownRef] = useState(false);
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    if (!once) return;
    if (hasShownRef) return;

    setHasShownRef(true);
    setVisible(true);

    const timer = setTimeout(() => {
      setVisible(false);
    }, duration);

    return () => clearTimeout(timer);
  }, [once, duration]);

  useEffect(() => {
    if (once) return;
    if (!open) {
      setVisible(false);
      return;
    }
    setVisible(true);

    const timer = setTimeout(() => {
      setVisible(false);
      onClose?.();
    }, duration);

    return () => clearTimeout(timer);
  }, [open, duration, onClose]);

  return (
    <>
      <div className={cn('fixed z-100', pos.container)}>
        <AnimatePresence>
          {visible && (
            <motion.div
              initial={{opacity: 0}}
              animate={{opacity: 1}}
              exit={{opacity: 0}}
              transition={{
                duration: 0.2,
                ease: 'easeOut',
              }}
              className="
                min-w-50 max-w-sm
                px-4 py-2.5
                rounded-lg
              bg-neutral-900 text-white
              dark:bg-neutral-100 dark:text-black
                text-sm font-medium tracking-tight
                shadow-md
                backdrop-blur
                "
            >
              {message}
            </motion.div>
          )}
        </AnimatePresence>
      </div>
    </>
  );
}

Toast Notification

A flexible, motion-enhanced notification system with support for multiple screen positions and smooth entry/exit animations.

Framer MotionNotificationFeedback UI