Success
For successful operations, confirmations, and positive feedback.
this.toastService.create({
type: 'success',
title: 'Success!',
message: 'Your changes have been saved.'
});Toast notification system inspired by Sonner. Features 5 toast types, 9 positioning options, stacking with hover expansion, and smooth animations. Built with Angular Signals.
npx wally-ui add toast
Add the Toast component once at the root level of your application.
// app.component.ts
import { Component } from '@angular/core';
import { Toast } from './components/wally-ui/toast/toast';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
imports: [Toast, RouterOutlet],
template: `
<!-- Add toast container once at root level -->
<wally-toast />
<!-- Rest of your app -->
<router-outlet />
`
})
export class AppComponent {}// In any component
import { ToastService } from './components/wally-ui/toast/lib/services/toast.service';
export class ExampleComponent {
private toastService = inject(ToastService);
showToast() {
this.toastService.create({
type: 'success',
message: 'Operation completed successfully!'
});
}
}For successful operations, confirmations, and positive feedback.
this.toastService.create({
type: 'success',
title: 'Success!',
message: 'Your changes have been saved.'
});For errors, failures, and critical issues that need attention.
this.toastService.create({
type: 'error',
title: 'Error',
message: 'Something went wrong. Please try again.'
});For informational messages and general notifications.
this.toastService.create({
type: 'info',
title: 'Info',
message: 'You have 3 unread notifications.'
});For warnings, cautions, and potential issues.
this.toastService.create({
type: 'warning',
title: 'Warning',
message: 'Your session will expire in 5 minutes.'
});For async operations. Loading toasts don't auto-dismiss and must be removed manually.
// Loading toast (no auto-dismiss)
const loadingToastId = this.toastService.create({
type: 'loading',
message: 'Processing your request...'
});
// Later, remove it manually
setTimeout(() => {
this.toastService.remove(loadingToastId);
}, 3000);For generic notifications without an icon. Neutral toasts don't auto-dismiss and must be removed manually.
// Neutral toast (no icon, no auto-dismiss)
const neutralToastId = this.toastService.create({
type: 'neutral',
title: 'Information',
message: 'This is a neutral toast without an icon.'
});
// Later, remove it manually
setTimeout(() => {
this.toastService.remove(neutralToastId);
}, 3000); Toasts can be positioned in 9 different locations. Default position is top-center.
Maximum 5 toasts per position. New toasts appear on top with subtle stacking effect. Hover to expand and see all toasts.
// Demonstrate stacking behavior
showMultipleToasts() {
// Create 5 toasts to see stacking
for (let i = 1; i <= 5; i++) {
setTimeout(() => {
this.toastService.create({
type: 'info',
title: `Toast #${i}`,
message: `This is toast number ${i}`,
duration: 10000
});
}, i * 500); // Stagger by 500ms
}
}// Form submission example
export class LoginComponent {
private toastService = inject(ToastService);
async onSubmit() {
const loadingId = this.toastService.create({
type: 'loading',
message: 'Signing in...'
});
try {
await this.authService.login(this.credentials);
// Remove loading toast
this.toastService.remove(loadingId);
// Show success
this.toastService.create({
type: 'success',
title: 'Welcome back!',
message: 'You have successfully signed in.'
});
} catch (error) {
// Remove loading toast
this.toastService.remove(loadingId);
// Show error
this.toastService.create({
type: 'error',
title: 'Login failed',
message: error.message
});
}
}
}// File upload with progress
export class UploadComponent {
private toastService = inject(ToastService);
async uploadFile(file: File) {
const uploadingId = this.toastService.create({
type: 'loading',
message: 'Uploading file...'
});
try {
await this.fileService.upload(file);
this.toastService.remove(uploadingId);
this.toastService.create({
type: 'success',
title: 'Upload complete',
message: `${file.name} has been uploaded successfully.`
});
} catch (error) {
this.toastService.remove(uploadingId);
this.toastService.create({
type: 'error',
title: 'Upload failed',
message: 'Failed to upload file. Please try again.'
});
}
}
}// Clipboard copy feedback
export class CodeExampleComponent {
private toastService = inject(ToastService);
async copyToClipboard(text: string) {
try {
await navigator.clipboard.writeText(text);
this.toastService.create({
type: 'success',
message: 'Copied to clipboard!',
duration: 2000,
position: 'bottom-center'
});
} catch (error) {
this.toastService.create({
type: 'error',
message: 'Failed to copy',
duration: 2000
});
}
}
}| Property | Type | Default | Description |
|---|---|---|---|
| type | 'success' | 'error' | 'info' | 'warning' | 'loading' | 'neutral' | required | Visual type and icon of the toast. Loading and neutral have no auto-dismiss |
| message | string | required | Main message text displayed in the toast |
| title | string | undefined | Optional bold title text displayed above the message |
| duration | number | 5000 | Auto-dismiss duration in milliseconds. Set to 0 for no auto-dismiss (manual close only) |
| position | ToastPosition | 'top-center' | Position on screen: 'top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right' |
| Method | Parameters | Returns | Description |
|---|---|---|---|
| create() | toast: Omit<Toast, 'id'> | number | Creates and displays a new toast. Returns the toast ID for manual removal (required for loading toasts) |
| remove() | id: number | void | Removes a toast by ID. Use this to manually dismiss loading or neutral toasts |
| pause() | id: number | void | Pauses auto-dismiss timer (used internally on hover). Calculates remaining time for accurate resume |
| resume() | id: number | void | Resumes auto-dismiss timer with remaining duration (used internally when hover ends) |
| setDefaultPosition() | position: ToastPosition | void | Sets the default position for all future toasts. Call during app initialization |
// ToastService.create() method signature
create(toast: {
type: 'success' | 'error' | 'info' | 'warning' | 'loading' | 'neutral';
message: string;
title?: string;
duration?: number; // milliseconds (default: 5000, 0 for no auto-dismiss)
position?: ToastPosition; // default: 'top-center'
}): number; // Returns toast ID// ToastService.remove() method signature
remove(id: number): void;
// Example
const toastId = this.toastService.create({
type: 'loading',
message: 'Loading...'
});
// Remove it later
this.toastService.remove(toastId);// ToastService.setDefaultPosition() method signature
setDefaultPosition(position: ToastPosition): void;
// Example
this.toastService.setDefaultPosition('bottom-right');// Available types
export type ToastType = 'success' | 'error' | 'info' | 'warning' | 'loading' | 'neutral';
export type ToastPosition =
| 'top-left' | 'top-center' | 'top-right'
| 'center-left' | 'center' | 'center-right'
| 'bottom-left' | 'bottom-center' | 'bottom-right';
export interface Toast {
id: number;
type: ToastType;
title?: string;
message: string;
duration?: number;
position?: ToastPosition;
}