Toast Component

August 8, 2025 ยท View on GitHub

An elegant and flexible toast notification system for Angular applications with signal-based architecture, automatic positioning, progress tracking, and smooth animations.

Overview

The Toast component provides a complete notification system that's perfect for displaying temporary messages, alerts, and user feedback. Built with modern Angular signals, it offers excellent performance and developer experience.

โœจ Key Features

  • ๐Ÿš€ Signal-Based Architecture: Built with Angular signals for optimal performance
  • ๐Ÿ“ Smart Positioning: Six position options with automatic layout management
  • โฑ๏ธ Progress Tracking: Visual progress bars showing remaining time
  • ๐ŸŽจ Type Variants: Success, error, warning, info, and default styles
  • ๐Ÿ–ฑ๏ธ Interactive: Optional click handlers and pause-on-hover functionality
  • โ™ฟ Accessible: Full keyboard navigation and screen reader support
  • ๐Ÿ“ฑ Responsive: Mobile-friendly design with proper touch interactions
  • ๐ŸŽญ Smooth Animations: Elegant slide-in/out animations with proper timing
  • ๐ŸŽ›๏ธ Highly Configurable: Extensive customization options
  • ๐Ÿ”ง Developer Friendly: Simple service-based API

๐Ÿ“ฆ Installation

The Toast component is part of the Angular SuperUI library. If you haven't installed the library yet:

npx ngsui-cli add toast

๐Ÿš€ Basic Usage

1. Import the Toast Module

import { ToastService, ToastContainer } from '@lib/components/toast';

@Component({
  standalone: true,
  imports: [ToastContainer], // Add ToastContainer to your app component
  // ... other config
})
export class AppComponent {
  private toastService = inject(ToastService);
}

2. Add Toast Container to Your Template

<!-- Add this to your app component template -->
<ToastContainer />

<!-- Your app content -->
<div class="app-content">
  <!-- ... -->
</div>

3. Show Toasts

export class MyComponent {
  private toastService = inject(ToastService);

  showSuccess() {
    this.toastService.success(
      'Success!', 
      'Your changes have been saved successfully.'
    );
  }

  showError() {
    this.toastService.error(
      'Error!', 
      'Something went wrong. Please try again.'
    );
  }

  showWarning() {
    this.toastService.warning(
      'Warning!', 
      'You have unsaved changes.'
    );
  }

  showInfo() {
    this.toastService.info(
      'Info', 
      'New feature available in settings.'
    );
  }
}

๐ŸŽ›๏ธ Advanced Configuration

Custom Toast Configuration

// Advanced toast with custom configuration
this.toastService.show({
  title: 'Custom Toast',
  description: 'This is a highly customized toast notification.',
  variant: 'success',
  position: 'top-right',
  duration: 5000,
  showIcon: true,
  showClose: true,
  showProgress: true,
  onClick: () => {
    console.log('Toast clicked!');
  }
});

Position Options

// Available positions
type ToastPosition = 
  | 'top-left' 
  | 'top-center' 
  | 'top-right'
  | 'bottom-left' 
  | 'bottom-center' 
  | 'bottom-right';

// Example usage
this.toastService.success('Title', 'Message', {
  position: 'bottom-right'
});

Persistent Toasts

// Toast that won't auto-dismiss
this.toastService.show({
  title: 'Important Notice',
  description: 'This requires your attention.',
  variant: 'warning',
  duration: 0, // 0 = persistent
  showClose: true
});

Interactive Toasts

// Toast with click handler
this.toastService.show({
  title: 'Update Available',
  description: 'Click to download the latest version.',
  variant: 'info',
  onClick: () => {
    // Handle click action
    window.open('/download', '_blank');
  }
});

๐ŸŽจ Component API

ToastService Methods

interface ToastService {
  // Convenience methods
  success(title: string, description?: string, config?: Partial<ToastConfig>): string;
  error(title: string, description?: string, config?: Partial<ToastConfig>): string;
  warning(title: string, description?: string, config?: Partial<ToastConfig>): string;
  info(title: string, description?: string, config?: Partial<ToastConfig>): string;
  
  // Full configuration method
  show(config: ToastConfig): string;
  
  // Management methods
  dismiss(id: string): void;
  dismissAll(): void;
  
  // Signals
  toasts: Signal<ToastItem[]>;
}

ToastConfig Interface

interface ToastConfig {
  // Content
  title: string;
  description?: string;
  
  // Appearance
  variant?: 'default' | 'success' | 'error' | 'warning' | 'info';
  showIcon?: boolean;
  showClose?: boolean;
  showProgress?: boolean;
  
  // Behavior
  duration?: number; // milliseconds, 0 = persistent
  position?: ToastPosition;
  
  // Interaction
  onClick?: () => void;
}

ToastContainer Props

interface ToastContainerProps {
  // Display options
  showProgress?: boolean; // Default: true
  maxToasts?: number;     // Default: 5
  
  // Custom classes
  containerClass?: string;
  toastClass?: string;
}

๐ŸŽญ Styling and Theming

CSS Custom Properties

.toast-container {
  --toast-bg: theme('colors.white');
  --toast-border: theme('colors.gray.200');
  --toast-shadow: theme('boxShadow.lg');
  --toast-border-radius: theme('borderRadius.xl');
  --toast-padding: theme('spacing.4');
  
  /* Dark mode */
  --toast-bg-dark: theme('colors.gray.800');
  --toast-border-dark: theme('colors.gray.700');
}

Custom Styling

@Component({
  template: `
    <ToastContainer 
      [containerClass]="'custom-toast-container'"
      [toastClass]="'custom-toast'"
    />
  `,
  styles: [`
    .custom-toast-container {
      z-index: 9999;
    }
    
    .custom-toast {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      border: none;
      color: white;
    }
  `]
})
export class CustomToastComponent {}

๐Ÿ”ง Advanced Examples

Toast with Loading State

showLoadingToast() {
  const toastId = this.toastService.show({
    title: 'Processing...',
    description: 'Please wait while we process your request.',
    variant: 'info',
    duration: 0, // Persistent
    showClose: false
  });

  // Simulate async operation
  setTimeout(() => {
    this.toastService.dismiss(toastId);
    this.toastService.success('Complete!', 'Request processed successfully.');
  }, 3000);
}

Toast Queue Management

showSequentialToasts() {
  const messages = [
    { title: 'Step 1', description: 'Initializing...' },
    { title: 'Step 2', description: 'Processing data...' },
    { title: 'Step 3', description: 'Finalizing...' },
    { title: 'Complete!', description: 'All steps finished.' }
  ];

  messages.forEach((message, index) => {
    setTimeout(() => {
      this.toastService.info(message.title, message.description, {
        duration: 2000
      });
    }, index * 1000);
  });
}

Toast with Undo Action

deleteWithUndo(itemId: string) {
  let isUndone = false;
  
  const toastId = this.toastService.show({
    title: 'Item Deleted',
    description: 'Click to undo this action.',
    variant: 'warning',
    duration: 5000,
    onClick: () => {
      if (!isUndone) {
        isUndone = true;
        this.restoreItem(itemId);
        this.toastService.dismiss(toastId);
        this.toastService.success('Restored', 'Item has been restored.');
      }
    }
  });
  
  // If not undone after toast expires, permanently delete
  setTimeout(() => {
    if (!isUndone) {
      this.permanentlyDeleteItem(itemId);
    }
  }, 5000);
}

โ™ฟ Accessibility Features

The Toast component is built with accessibility in mind:

  • Screen Reader Support: Proper ARIA labels and live regions
  • Keyboard Navigation: Full keyboard control for interactive elements
  • Focus Management: Proper focus handling for dismissible toasts
  • High Contrast: Supports high contrast mode
  • Reduced Motion: Respects prefers-reduced-motion setting

ARIA Attributes

<!-- Generated toast markup -->
<div
  role="alert"
  aria-live="polite"
  aria-describedby="toast-description"
  class="toast"
>
  <h4 id="toast-title">Toast Title</h4>
  <p id="toast-description">Toast description</p>
  <button 
    aria-label="Close notification"
    class="toast-close"
  >
    ร—
  </button>
</div>

๐ŸŽฏ Best Practices

1. Toast Timing

// Good: Appropriate timing for different types
this.toastService.success('Saved!', 'Document saved.', { duration: 2000 });
this.toastService.error('Error!', 'Check details.', { duration: 4000 });
this.toastService.warning('Warning!', 'Action needed.', { duration: 0 }); // Persistent

2. Content Guidelines

// Good: Clear, concise messaging
this.toastService.success(
  'Payment Successful', 
  'Your order #12345 has been confirmed.'
);

// Avoid: Vague or overly technical messages
this.toastService.error(
  'HTTP 500 Internal Server Error', 
  'SQLException: Connection timeout...'
);

3. Position Strategy

// Good: Consistent positioning
const TOAST_CONFIG = {
  position: 'top-right' as const,
  duration: 3000
};

this.toastService.success('Title', 'Message', TOAST_CONFIG);

4. Error Handling

async performAction() {
  try {
    await this.apiService.updateData();
    this.toastService.success('Updated!', 'Data saved successfully.');
  } catch (error) {
    this.toastService.error(
      'Update Failed', 
      'Please check your connection and try again.'
    );
  }
}

๐Ÿ” Troubleshooting

Common Issues

  1. Toast not appearing

    // Ensure ToastContainer is in your template
    @Component({
      template: `<ToastContainer />` // Must be present
    })
    
  2. Toasts not positioning correctly

    /* Ensure proper z-index */
    .toast-container {
      z-index: 9999;
      position: fixed;
    }
    
  3. Progress bar not updating

    // Ensure showProgress is enabled
    this.toastService.show({
      title: 'Test',
      showProgress: true // Add this
    });
    

๐Ÿš€ Performance Tips

  1. Limit active toasts

    <ToastContainer [maxToasts]="3" />
    
  2. Use appropriate durations

    // Short messages
    this.toastService.success('Saved!', undefined, { duration: 2000 });
    
    // Important messages
    this.toastService.error('Error!', 'Details...', { duration: 5000 });
    
  3. Clean up persistent toasts

    ngOnDestroy() {
      this.toastService.dismissAll();
    }
    

๐Ÿค Contributing

We welcome contributions! Please see our Contributing Guide for details.

๐Ÿ“ License

This component is part of Angular SuperUI and is licensed under the MIT License.