ngx-toastr vs @ngneat/hot-toast
Angular Toast Notifications: Modern vs Legacy Implementation
ngx-toastr@ngneat/hot-toastSimilar Packages:

Angular Toast Notifications: Modern vs Legacy Implementation

@ngneat/hot-toast and ngx-toastr are both popular libraries for displaying non-blocking notification popups in Angular applications. ngx-toastr is the legacy standard, known for its stability and extensive configuration options built around Angular modules. @ngneat/hot-toast is a modern alternative inspired by React's hot-toast, designed for standalone components, functional providers, and a simpler API surface. Both handle success, error, warning, and info states, but they differ significantly in setup, customization, and alignment with recent Angular versions.

Npm Package Weekly Downloads Trend

3 Years

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
ngx-toastr543,4532,578175 kB788 months agoMIT
@ngneat/hot-toast0-401 kB-3 years agoMIT

Angular Toast Notifications: Modern vs Legacy Implementation

Both @ngneat/hot-toast and ngx-toastr solve the same problem β€” showing temporary feedback messages to users without blocking their workflow. However, they reflect different eras of Angular development. ngx-toastr represents the classic Module-based approach, while @ngneat/hot-toast embraces the modern Standalone and Functional API style. Let's compare how they handle setup, usage, and customization.

πŸ› οΈ Setup and Configuration

@ngneat/hot-toast uses functional providers, which fits cleanly into modern app.config.ts files.

  • You configure it once using provideHotToastConfig().
  • No need for NgModules in most cases.
// app.config.ts
import { provideHotToastConfig } from '@ngneat/hot-toast';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHotToastConfig({
      position: 'top-center',
      duration: 3000
    })
  ]
};

ngx-toastr relies on traditional NgModules and requires BrowserAnimationsModule.

  • You must import ToastrModule.forRoot() in your app module.
  • Requires extra setup for standalone apps.
// app.module.ts
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';

@NgModule({
  imports: [
    BrowserAnimationsModule,
    ToastrModule.forRoot({
      positionClass: 'toast-top-center',
      timeOut: 3000
    })
  ]
})
export class AppModule {}

πŸ“’ Triggering Notifications

@ngneat/hot-toast uses a simple service object imported directly.

  • You call methods like toast.success() anywhere in your code.
  • No need to inject a service into constructors.
// component.ts
import { toast } from '@ngneat/hot-toast';

function saveData() {
  toast.success('Data saved successfully');
  // Or with options
  toast.error('Failed to save', { duration: 5000 });
}

ngx-toastr requires injecting the ToastrService into your class.

  • You call methods on the injected instance.
  • More boilerplate but explicit dependencies.
// component.ts
import { ToastrService } from 'ngx-toastr';

@Component({ ... })
export class MyComponent {
  constructor(private toastr: ToastrService) {}

  saveData() {
    this.toastr.success('Data saved successfully');
    // Or with options
    this.toastr.error('Failed to save', 'Error', { timeOut: 5000 });
  }
}

🎨 Custom Content and Components

@ngneat/hot-toast allows passing Angular components directly to the toast.

  • You can pass a component class and data inputs.
  • Great for complex notifications with buttons or forms.
// component.ts
import { toast } from '@ngneat/hot-toast';
import { CustomComponent } from './custom.component';

function showCustom() {
  toast.show(CustomComponent, {
    data: { message: 'Action required' },
    duration: 10000
  });
}

ngx-toastr typically uses TemplateRef or ComponentPortal.

  • You often need to define a template in your HTML first.
  • More verbose for custom UIs.
// component.ts
import { ToastrService } from 'ngx-toastr';
import { TemplateRef } from '@angular/core';

@Component({ ... })
export class MyComponent {
  constructor(private toastr: ToastrService) {}

  showCustom(templateRef: TemplateRef<any>) {
    this.toastr.show(templateRef, 'Custom Title', {
      toastClass: 'custom-toast'
    });
  }
}

πŸ“ Positioning and Styling

@ngneat/hot-toast handles positioning via config objects.

  • Uses simple string values like 'top-center' or 'bottom-right'.
  • Styles are easily overridden via the style option.
// component.ts
import { toast } from '@ngneat/hot-toast';

toast.success('Moved to bottom', {
  position: 'bottom-center',
  style: { background: 'blue', color: 'white' }
});

ngx-toastr uses CSS classes for positioning.

  • You set classes like 'toast-top-right' in config.
  • Requires global CSS setup for custom themes.
// component.ts
import { ToastrService } from 'ngx-toastr';

this.toastr.success('Moved to bottom', '', {
  positionClass: 'toast-bottom-center',
  toastClass: 'blue-theme'
});

⏳ Loading States

@ngneat/hot-toast has built-in loading state management.

  • You can start a loader and resolve it later by ID.
  • Perfect for async operations like API calls.
// component.ts
import { toast } from '@ngneat/hot-toast';

async function loadData() {
  const loadingId = toast.load('Loading data...');
  
  try {
    await api.fetch();
    toast.success('Done', { id: loadingId });
  } catch {
    toast.error('Failed', { id: loadingId });
  }
}

ngx-toastr does not have built-in loading state transitions.

  • You must manually hide one toast and show another.
  • Requires tracking IDs manually if you want to replace them.
// component.ts
import { ToastrService } from 'ngx-toastr';

async function loadData() {
  const loading = this.toastr.info('Loading data...');
  
  try {
    await api.fetch();
    this.toastr.remove(loading.toastId);
    this.toastr.success('Done');
  } catch {
    this.toastr.remove(loading.toastId);
    this.toastr.error('Failed');
  }
}

🌱 Similarities: Shared Ground

While the implementation differs, both libraries share core goals and capabilities.

1. πŸ“¦ Standard Notification Types

  • Both support Success, Error, Warning, and Info out of the box.
  • Colors and icons are pre-configured for each type.
// @ngneat/hot-toast
toast.success('OK');
toast.error('Fail');

// ngx-toastr
this.toastr.success('OK');
this.toastr.error('Fail');

2. ⏱️ Auto-Dismissal

  • Both allow setting a duration before the toast disappears.
  • Users can often click to dismiss early.
// @ngneat/hot-toast
toast.show('Message', { duration: 4000 });

// ngx-toastr
this.toastr.show('Message', '', { timeOut: 4000 });

3. 🚫 Disable Auto-Close

  • Both support persistent toasts that stay until clicked.
  • Useful for critical errors requiring user acknowledgment.
// @ngneat/hot-toast
toast.error('Critical', { duration: 0 });

// ngx-toastr
this.toastr.error('Critical', '', { disableTimeOut: true });

4. β™Ώ Accessibility

  • Both use ARIA live regions to announce messages to screen readers.
  • Ensures compliance with basic accessibility standards.
// Both handle this internally via role="alert"
// No extra code needed for basic support

πŸ“Š Summary: Key Differences

Feature@ngneat/hot-toastngx-toastr
SetupπŸš€ Functional providers🧩 NgModules
UsageπŸ“¦ Imported service objectπŸ’‰ Injected service
Custom UI🎨 Pass Component classπŸ–ΌοΈ TemplateRef or Portal
Loading Stateβœ… Built-in resolve/update❌ Manual remove/show
StylingπŸ–ŒοΈ Inline style options🎭 CSS Classes
Modern Angularβœ… Standalone ready⚠️ Requires adaptation

πŸ’‘ The Big Picture

@ngneat/hot-toast is like a modern utility belt πŸŽ’ β€” lightweight, easy to grab, and designed for speed. It removes boilerplate and fits naturally into new Angular projects. Choose this for greenfield development where you want less configuration and more code.

ngx-toastr is like a heavy-duty toolbox 🧰 β€” robust, familiar, and everywhere. It has been around for years and works reliably in complex enterprise apps. Choose this for maintaining existing systems or if your team prefers traditional dependency injection patterns.

Final Thought: Both libraries will get the job done, but @ngneat/hot-toast reduces friction for modern Angular developers. If you are starting fresh, the simpler API and standalone support make it the logical choice β€” unless you are already locked into the ngx-toastr ecosystem.

How to Choose: ngx-toastr vs @ngneat/hot-toast

  • ngx-toastr:

    Choose ngx-toastr if you are maintaining a legacy Angular application or need a battle-tested solution with a massive existing ecosystem. It is suitable for projects already using NgModules where migrating to a new notification system introduces unnecessary risk. It remains a solid choice for stable, long-term projects that do not require the latest Angular features.

  • @ngneat/hot-toast:

    Choose @ngneat/hot-toast if you are building a new Angular application using standalone components or recent versions (15+). It offers a simpler, function-based API that aligns with modern Angular practices like functional providers and signals. It is ideal for teams wanting less boilerplate and easier custom component integration without dealing with complex module imports.

README for ngx-toastr

Angular Toastr

ngx-toastr


npm codecov

DEMO: https://ngx-toastr.vercel.app

Features

  • Toast Component Injection without being passed ViewContainerRef
  • No use of @for. Fewer dirty checks and higher performance
  • No use of @angular/animations
  • AoT compilation and lazy loading compatible
  • Component inheritance for custom toasts
  • SystemJS/UMD rollup bundle
  • Output toasts to an optional target directive

Dependencies

Latest version available for each version of Angular

ngx-toastrAngular
13.2.110.x 11.x
14.3.012.x 13.x
15.2.214.x.
16.2.015.x
17.0.216.x
18.x 19x>= 17.x < 23.x
current>= 20.x

Install

npm install ngx-toastr --save

Setup

step 1: add css

  • copy toast css to your project.
  • If you are using sass you can import the css.
// regular style toast
@import 'ngx-toastr/toastr';

// bootstrap style toast
// or import a bootstrap 4 alert styled design (SASS ONLY)
// should be after your bootstrap imports, it uses bs4 variables, mixins, functions
@import 'ngx-toastr/toastr-bs4-alert';

// if you'd like to use it without importing all of bootstrap it requires
@import 'bootstrap/scss/functions';
@import 'bootstrap/scss/variables';
@import 'bootstrap/scss/mixins';
// bootstrap 4
@import 'ngx-toastr/toastr-bs4-alert';
// boostrap 5
@import 'ngx-toastr/toastr-bs5-alert';
  • If you are using angular-cli you can add it to your angular.json
"styles": [
  "styles.scss",
  "node_modules/ngx-toastr/toastr.css" // try adding '../' if you're using angular cli before 6
]

step 2: add ToastrModule to app NgModule, or provideToastr to providers.

  • Module based
import { ToastrModule } from 'ngx-toastr';

@NgModule({
  imports: [
    ToastrModule.forRoot(), // ToastrModule added
  ],
  bootstrap: [App],
  declarations: [App],
})
class MainModule {}
  • Standalone
import { AppComponent } from './src/app.component';
import { provideToastr } from 'ngx-toastr';

bootstrapApplication(AppComponent, {
  providers: [
    provideToastr(), // Toastr providers
  ]
});

Use

import { ToastrService } from 'ngx-toastr';
import { inject } from '@angular/core';

@Component({...})
export class YourComponent {
  toastr = inject(ToastrService);

  showSuccess() {
    this.toastr.success('Hello world!', 'Toastr fun!');
  }
}

Options

There are individual options and global options.

Individual Options

Passed to ToastrService.success/error/warning/info/show()

OptionTypeDefaultDescription
toastComponentComponentToastAngular component that will be used
closeButtonbooleanfalseShow close button
timeOutnumber5000Time to live in milliseconds
extendedTimeOutnumber1000Time to close after a user hovers over toast
disableTimeOutboolean | 'timeOut' | 'extendedTimeOut'falseDisable both timeOut and extendedTimeOut when set to true. Allows specifying which timeOut to disable, either: timeOut or extendedTimeOut
easingstring'ease-in'Toast component easing
easeTimestring | number300Time spent easing
enableHtmlbooleanfalseAllow html in message
newestOnTopbooleantrueNew toast placement
progressBarbooleanfalseShow progress bar
progressAnimation'decreasing' | 'increasing''decreasing'Changes the animation of the progress bar.
toastClassstring'ngx-toastr'CSS class(es) for toast
positionClassstring'toast-top-right'CSS class(es) for toast container
titleClassstring'toast-title'CSS class(es) for inside toast on title
messageClassstring'toast-message'CSS class(es) for inside toast on message
tapToDismissbooleantrueClose on click
onActivateTickbooleanfalseFires changeDetectorRef.detectChanges() when activated. Helps show toast from asynchronous events outside of Angular's change detection

Setting Individual Options

success, error, info, warning take (message, title, ToastConfig) pass an options object to replace any default option.

this.toastrService.error('everything is broken', 'Major Error', {
  timeOut: 3000,
});

Global Options

All individual options can be overridden in the global options to affect all toasts. In addition, global options include the following options:

OptionTypeDefaultDescription
maxOpenednumber0Max toasts opened. Toasts will be queued. 0 is unlimited
autoDismissbooleanfalseDismiss current toast when max is reached
iconClassesobjectsee belowClasses used on toastr service methods
preventDuplicatesbooleanfalseBlock duplicate messages
countDuplicatesbooleanfalseDisplays a duplicates counter (preventDuplicates must be true). Toast must have a title and duplicate message
resetTimeoutOnDuplicatebooleanfalseReset toast timeout on duplicate (preventDuplicates must be true)
includeTitleDuplicatesbooleanfalseInclude the title of a toast when checking for duplicates (by default only message is compared)
iconClasses defaults
iconClasses = {
  error: 'toast-error',
  info: 'toast-info',
  success: 'toast-success',
  warning: 'toast-warning',
};

Setting Global Options

Pass values to ToastrModule.forRoot() or provideToastr() to set global options.

  • Module based
// root app NgModule
imports: [
  ToastrModule.forRoot({
    timeOut: 10000,
    positionClass: 'toast-bottom-right',
    preventDuplicates: true,
  }),
],
  • Standalone
import { AppComponent } from './src/app.component';
import { provideToastr } from 'ngx-toastr';

bootstrapApplication(AppComponent, {
  providers: [
    provideToastr({
      timeOut: 10000,
      positionClass: 'toast-bottom-right',
      preventDuplicates: true,
    }), 
  ]
});

Toastr Service methods return:

export interface ActiveToast {
  /** Your Toast ID. Use this to close it individually */
  toastId: number;
  /** the title of your toast. Stored to prevent duplicates if includeTitleDuplicates set */
  title: string;
  /** the message of your toast. Stored to prevent duplicates */
  message: string;
  /** a reference to the component see portal.ts */
  portal: ComponentRef<any>;
  /** a reference to your toast */
  toastRef: ToastRef<any>;
  /** triggered when toast is active */
  onShown: Observable<any>;
  /** triggered when toast is destroyed */
  onHidden: Observable<any>;
  /** triggered on toast click */
  onTap: Observable<any>;
  /** available for your use in custom toast */
  onAction: Observable<any>;
}

Put toasts in your own container

Put toasts in a specific div inside your application. This should probably be somewhere that doesn't get deleted. Add ToastContainerDirective to the ngModule where you need the directive available. Make sure that your container has an aria-live="polite" attribute, so that any time a toast is injected into the container it is announced by screen readers.

import { NgModule } from '@angular/core';
import { ToastrModule, ToastContainerDirective } from 'ngx-toastr';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [
    ToastrModule.forRoot({ positionClass: 'inline' }),
    ToastContainerDirective,
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Add a div with toastContainer directive on it.

import { Component, OnInit, viewChild, inject } from '@angular/core';
import { ToastContainerDirective, ToastrService } from 'ngx-toastr';

@Component({
  selector: 'app-root',
  template: `
    <h1><a (click)="onClick()">Click</a></h1>
    <div aria-live="polite" toastContainer></div>
  `,
})
export class AppComponent implements OnInit {
  toastContainer = viewChild(ToastContainerDirective, { static: true });
  toastrService = inject(ToastrService);

  ngOnInit() {
    this.toastrService.overlayContainer = this.toastContainer;
  }
  onClick() {
    this.toastrService.success('in div');
  }
}

Functions

Clear

Remove all or a single toast by optional id

toastrService.clear(toastId?: number);
Remove

Remove and destroy a single toast by id

toastrService.remove(toastId: number);

SystemJS

If you are using SystemJS, you should also adjust your configuration to point to the UMD bundle.

In your SystemJS config file, map needs to tell the System loader where to look for ngx-toastr:

map: {
  'ngx-toastr': 'node_modules/ngx-toastr/bundles/ngx-toastr.umd.min.js',
}

Setup Without Animations

If you do not want animations you can override the default toast component in the global config to use ToastNoAnimation instead of the default one.

In your main module (ex: app.module.ts)

import { ToastrModule, ToastNoAnimation, ToastNoAnimationModule } from 'ngx-toastr';

@NgModule({
  imports: [
    // ...
    ToastNoAnimationModule.forRoot(),
  ],
  // ...
})
class AppModule {}

That's it! No animations.

Using A Custom Toast

Create your toast component extending Toast see the demo's pink toast for an example https://github.com/scttcper/ngx-toastr/blob/master/src/app/pink.toast.ts

import { ToastrModule } from 'ngx-toastr';

@NgModule({
  imports: [
    ToastrModule.forRoot({
      toastComponent: YourToastComponent, // added custom toast!
    }),
  ],
  bootstrap: [App],
  declarations: [App, YourToastComponent], // add!
})
class AppModule {}

FAQ

  1. ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked
    When opening a toast inside an angular lifecycle wrap it in setTimeout
ngOnInit() {
    setTimeout(() => this.toastr.success('sup'))
}
  1. Change default icons (check, warning sign, etc)
    Overwrite the css background-image: https://github.com/scttcper/ngx-toastr/blob/master/src/lib/toastr.css.
  2. How do I use this in an ErrorHandler?
    See: https://github.com/scttcper/ngx-toastr/issues/179.
  3. How can I translate messages?
    See: https://github.com/scttcper/ngx-toastr/issues/201.
  4. How to handle toastr click/tap action?
    showToaster() {
      this.toastr.success('Hello world!', 'Toastr fun!')
        .onTap
        .pipe(take(1))
        .subscribe(() => this.toasterClickedHandler());
    }
    
    toasterClickedHandler() {
      console.log('Toastr clicked');
    }
    
  5. How to customize styling without overridding defaults?
    Add multiple CSS classes separated by a space:
    toastClass: 'yourclass ngx-toastr'
    
    See: https://github.com/scttcper/ngx-toastr/issues/594.

Previous Works

toastr original toastr
angular-toastr AngularJS toastr
notyf notyf (css)

License

MIT


GitHub @scttcper Β Β·Β  Twitter @scttcper