Skip to content

ngxpert/hot-toast

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

264 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 


npm MIT commitizen PRs styled with prettier linted with eslint All Contributors ngxpert cypress semantic-release

Smoking hot Notifications for Angular. Lightweight, customizable and beautiful by default. Inspired from react-hot-toast

Screen.Recording.2024-02-19.at.1.27.10.PM.mov

Sponsoring ngxpert

Sponsorships aid in the continued development and maintenance of ngxpert libraries. Consider asking your company to sponsor ngxpert as its core to their business and application development.


Compatibility with Angular Versions

@ngxpert/hot-toast Angular
1.x, 2.x >= 17 < 18
3.x >= 18 < 19
4.x >= 19 < 20
5.x >= 20 < 21
6.x >= 21

For older Angular versions, keep using @ngneat/hot-toast from npm.

Features

  • πŸ”₯ Hot by default
  • β˜• Easy to use
  • 🐍 Snackbar variation
  • β™Ώ Accessible
  • πŸ–οΈ Reduce motion support
  • 😊 Emoji Support
  • πŸ›  Customizable
  • ⏳ Observable API
  • βœ‹ Pause on hover
  • πŸ” Events
  • πŸ”’ Persistent
  • 🎭 Grouping
  • πŸ”’ CSP Compatible
  • πŸ“¦ Customizable Toast Container
  • πŸ’¬ Popover API
  • 🎨 Built-in Themes β€” Material, Minimal, Glassmorphism, iOS (with manual dark mode)
  • 🌐 Optional HTTP error interceptor β€” show error toasts on failed HttpClient calls with ignore rules
  • πŸ—’οΈ Form Integration β€” reactively show validation toasts from any AbstractControl or FormGroup

Installation

Angular 19+

Using Angular CLI:

ng add @ngxpert/hot-toast

Other Angular Versions

With npm:

npm install @ngneat/overview@7.0.0 @ngxpert/hot-toast

or yarn

yarn add @ngneat/overview@7.0.0 @ngxpert/hot-toast
For older versions
# For Angular version >= 9.1.13 < 13
npm install @ngneat/overview@2.0.2 @ngneat/hot-toast@3

# For Angular version >= 13 < 15
npm install @ngneat/overview@3.0.0 @ngneat/hot-toast@4

# For Angular version >= 15 <16
npm install @ngneat/overview@3.0.0 @ngneat/hot-toast@5

# For Angular version >= 16 <17
npm install @ngneat/overview@5.1.1 @ngneat/hot-toast@6

# For Angular version >= 17 <18
npm install @ngneat/overview@6.0.0 @ngxpert/hot-toast@2

# For Angular version >= 18 <19
npm install @ngneat/overview@6.1.1 @ngxpert/hot-toast@3

# For Angular version >= 19 <20
npm install @ngneat/overview@6.1.1 @ngxpert/hot-toast@4

# For Angular version >= 20 <21
npm install @ngneat/overview@6.1.1 @ngxpert/hot-toast@5

# For Angular version >= 21
npm install @ngneat/overview@7.0.0 @ngxpert/hot-toast@6

Setup

Step 1a/2: Standalone Setup

import { AppComponent } from './src/app.component';

import { provideHotToastConfig } from '@ngxpert/hot-toast';

bootstrapApplication(AppComponent, {
  providers: [
    provideHotToastConfig(), // @ngxpert/hot-toast providers
  ],
});

Step 1b/2: Module Setup

Add provideHotToastConfig() to your app.module.ts providers section. Toast options (Partial<ToastConfig>) here.:

import { provideHotToastConfig } from '@ngxpert/hot-toast';

@NgModule({
  providers: [provideHotToastConfig()],
})
class AppModule {}

Step 2/2: Stylings

Add the base styles β€” required for all themes.

If you use SCSS, add this to your main styles.scss:

@use '@ngxpert/hot-toast/styles';

Or if you use CSS, add this inside angular.json:

"styles": [
  "@ngxpert/hot-toast/styles.css"
]

Tip β€” ng add handles this automatically. Running ng add @ngxpert/hot-toast injects the base import into your styles file and prompts you to pick a theme.

Optionally add a built-in theme

Import one theme stylesheet on top of the base. See the Built-in Themes section for all available themes.

SCSS:

@use '@ngxpert/hot-toast/styles';
@use '@ngxpert/hot-toast/themes/material'; /* or minimal | glassmorphism | ios */

CSS / angular.json:

"styles": [
  "@ngxpert/hot-toast/styles.css",
  "@ngxpert/hot-toast/themes/material.css"
]

Basic Usage

import { HotToastService } from '@ngxpert/hot-toast';

@Component({})
export class AppComponent {
  constructor(private toast: HotToastService) {}

  showToast() {
    this.toast.show('Hello World!');
    this.toast.loading('Lazyyy...');
    this.toast.success('Yeah!!');
    this.toast.warning('Boo!');
    this.toast.error('Oh no!');
    this.toast.info('Something...');
  }

  update() {
    saveSettings
      .pipe(
        this.toast.observe({
          loading: 'Saving...',
          success: 'Settings saved!',
          error: 'Could not save.',
        }),
      )
      .subscribe();
  }
}

You can pass ToastOptions while creating the toast to customize the look and behavior:

import { HotToastService } from '@ngxpert/hot-toast';

@Component({})
export class AppComponent {
  constructor(private toast: HotToastService) {}

  customToast() {
    this.toast.success('Look at my styles, and I also need more time!', {
      duration: 5000,
      style: {
        border: '1px solid #713200',
        padding: '16px',
        color: '#713200',
      },
      iconTheme: {
        primary: '#713200',
        secondary: '#FFFAEE',
      },
    });
  }
}

You can also set global ToastConfig options while importing:

import { provideHotToastConfig } from '@ngxpert/hot-toast';

@NgModule({
  providers: [
    provideHotToastConfig({
      reverseOrder: true,
      dismissible: true,
      autoClose: false,
    }),
  ],
})
class AppModule {}

Additionally, you have the option of using a standalone function to provide a global toast configuration within your app's configuration file:

// app.config.ts
import { provideHotToastConfig } from '@ngxpert/hot-toast';

export const appConfig: ApplicationConfig = {
  providers: [provideHotToastConfig({ ... })],
};

Built-in Themes

Hot Toast ships with 6 themes. The toast and snackbar themes are included in the base styles β€” no extra import needed. The four new themes each have a dedicated stylesheet so your bundle only includes what you use.

Theme Value Extra stylesheet needed? Visual character
Default toast No Clean white card, subtle shadow
Snackbar snackbar No Dark surface, bottom-center friendly
Material material Yes Material elevation-2 shadow, 4 px radius
Minimal minimal Yes No shadow, 1 px border, understated
Glassmorphism glassmorphism Yes backdrop-filter: blur(12px), semi-transparent
iOS ios Yes backdrop-filter: blur(20px), pill shape

Importing a theme

SCSS β€” add both the base and your chosen theme to styles.scss:

@use '@ngxpert/hot-toast/styles';
@use '@ngxpert/hot-toast/themes/material'; /* or minimal | glassmorphism | ios */

CSS / angular.json β€” add the compiled CSS files to the styles array:

"styles": [
  "@ngxpert/hot-toast/styles.css",
  "@ngxpert/hot-toast/themes/material.css"
]

Applying a theme

Per-toast β€” pass the theme option when creating a toast:

toast.success('Saved!', { theme: 'material' });
toast.info('Note', { theme: 'minimal' });
toast.show('Glass', { theme: 'glassmorphism', icon: 'πŸͺŸ' });
toast.success('Done', { theme: 'ios' });

Globally β€” set the default theme for all toasts via provideHotToastConfig:

// app.config.ts
import { provideHotToastConfig } from '@ngxpert/hot-toast';

export const appConfig = {
  providers: [provideHotToastConfig({ theme: 'material' })],
};

Individual toast calls can still override the global default with their own theme option.

Dark mode

For apps with a manual dark-mode toggle, add the hot-toast-dark-theme class to any parent element (e.g. <body>) β€” it forces dark mode on every themed toast inside it regardless of OS preference:

<body class="hot-toast-dark-theme">
  <!-- all Material / Minimal / Glassmorphism / iOS toasts render in dark mode -->
</body>

ng add theme prompt

When installing via ng add, the schematic asks which theme to set up:

ng add @ngxpert/hot-toast
# > Choose a built-in theme: None | Material | Minimal | Glassmorphism | iOS

# Non-interactive:
ng add @ngxpert/hot-toast --theme=material

The schematic injects both the base stylesheet and the chosen theme import into your project automatically.


HTTP error interceptor (optional)

Register the functional interceptor on HttpClient so failed requests open an error toast and are still rethrown to your subscribe / catchError handlers. Use provideHotToastHttpInterceptor() for optional rules such as skipping specific status codes (for example 401).

import { hotToastHttpInterceptor, provideHotToastHttpInterceptor, provideHotToastConfig } from '@ngxpert/hot-toast';
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';

bootstrapApplication(AppComponent, {
  providers: [
    provideHotToastConfig(),
    provideHttpClient(withFetch(), withInterceptors([hotToastHttpInterceptor])),
    provideHotToastHttpInterceptor({
      ignoreStatuses: [401],
    }),
  ],
});

Add hotToastHttpInterceptor to the same provideHttpClient(..., withInterceptors([...])) call as your other features (for example withFetch()). Configuration is merged from defaults plus provideHotToastHttpInterceptor({ ... }); see HotToastHttpInterceptorConfig in the library source for ignoreStatuses, shouldIgnore, skipRequest, errorMessage, and toastOptions.

Docs on the demo site: https://ngxpert.github.io/hot-toast#http-interceptor.

Try it in this repository

  1. Install and start the sample Express API (default http://localhost:4000):

    npm run sample:http-test-server:install
    npm run sample:http-test-server
  2. In another terminal, start the docs app (npm start, default http://localhost:4200).

  3. Open http://localhost:4200/http-interceptor-sample. The app uses environment.sampleHttpTestServerUrl (http://localhost:4000 in development; empty in production builds unless you override it).

End-to-end coverage lives in cypress/e2e/toast_http_interceptor.cy.ts. More detail: samples/express-http-test-server/README.md.

Form Integration (optional)

fromForm subscribes to an AbstractControl's statusChanges stream and shows a single, in-place toast that updates as the control transitions through Angular's four statuses: VALID, INVALID, PENDING, and DISABLED.

Peer dependency β€” @angular/forms must be installed (it is an optional peer dependency of @ngxpert/hot-toast).

Basic usage

import { FormControl, Validators } from '@angular/forms';
import { HotToastService } from '@ngxpert/hot-toast';

@Component({ ... })
export class SignUpComponent implements OnDestroy {
  private toast = inject(HotToastService);

  email = new FormControl('', [Validators.required, Validators.email]);

  private formRef = this.toast.fromForm(this.email, {
    INVALID: { message: 'Enter a valid email address.' },
    VALID:   { message: 'Email looks good!', duration: 2000 },
  });

  ngOnDestroy() {
    this.formRef.close(); // unsubscribes and dismisses any active toast
  }
}

Async validators + PENDING state

email = new FormControl('', {
  validators: [Validators.required, Validators.email],
  asyncValidators: [this.checkEmailTaken()],
});

private formRef = this.toast.fromForm(this.email, {
  PENDING: { message: 'Checking availability…' },
  INVALID: {
    message: (ctrl) => {
      if (ctrl.errors?.['required']) return 'Email is required';
      if (ctrl.errors?.['email'])    return 'Enter a valid email';
      if (ctrl.errors?.['taken'])    return 'That email is already taken';
    },
  },
  VALID: { message: 'Email is available!', duration: 2000 },
});

Gating toasts behind form submission

Use the show predicate to suppress toasts until the user has explicitly submitted the form:

submitted = false;

private formRef = this.toast.fromForm(this.email, {
  PENDING: { message: 'Checking…',              show: () => this.submitted },
  INVALID: { message: 'Please fix the errors.', show: () => this.submitted },
  VALID:   { message: 'All good!',              show: () => this.submitted, duration: 2000 },
});

submit() {
  this.submitted = true;
  this.email.updateValueAndValidity();
}

FormGroup support

fromForm accepts any AbstractControl, including FormGroup:

form = new FormGroup({
  name:     new FormControl('', [Validators.required, Validators.minLength(3)]),
  password: new FormControl('', [Validators.required, Validators.minLength(6)]),
});

submitted = false;

private formRef = this.toast.fromForm(this.form, {
  INVALID: {
    message: (form) => {
      const c = form.controls;
      if (c.name.errors?.['required'])      return 'Name is required';
      if (c.name.errors?.['minlength'])     return 'Name needs 3+ characters';
      if (c.password.errors?.['required'])  return 'Password is required';
      if (c.password.errors?.['minlength']) return 'Password needs 6+ characters';
    },
    show: () => this.submitted,
  },
  VALID: { message: 'Form is valid!', duration: 2000, show: () => this.submitted },
});

FormToastOptions API

Each key in FormToastOptions corresponds to a FormControlStatus value. All keys are optional β€” omitting a key means no toast is shown for that status.

Key Trigger
VALID Control/group passes all validators
INVALID One or more validators fail
PENDING Async validators are running
DISABLED Control is disabled

Each key accepts a FormToastStateConfig:

Property Type Description
message Content | (control) => Content Static text/template or a function receiving the full AbstractControl.
show (control) => boolean | Promise<boolean> Optional predicate that gates display. If omitted the toast always shows for that status. Async predicates are supported.
...ToastOptions β€” All standard ToastOptions (type, duration, dismissible, position, etc.) are accepted alongside message and show for per-state overrides.

HotToastFormRef

fromForm returns a HotToastFormRef:

const ref = this.toast.fromForm(control, options);

// Unsubscribes from statusChanges and dismisses any active toast
ref.close();

Always call ref.close() when the component is destroyed to prevent subscription leaks.

Default toast type per status

When no explicit type is provided in a state config, the library falls back to sensible defaults defined in HOT_TOAST_FORM_STATUS_DEFAULTS (in constants.ts):

Status Default type Default autoClose
VALID success true
INVALID error false
PENDING loading false
DISABLED blank true

Live demo: https://ngxpert.github.io/hot-toast#form-integration


Examples

You can checkout examples at: https://ngxpert.github.io/hot-toast#examples.

ToastConfig

All options, which are set Available in global config? from ToastOptions are supported. Below are extra configurable options:

Name Type Description
reverseOrder boolean Sets the reverse order for hot-toast stacking
Default: false
visibleToasts number Sets the number of toasts visible. 0 will set no limit.
Default: 5
stacking "vertical"|"depth" Sets Sets the type of stacking
Default: "vertical"
usePopover boolean Sets whether to use the popover API
Default: undefined

ToastOptions

Configuration used when opening an hot-toast.

Name Type Description Available in global config?
id string Unique id to associate with hot-toast. There can't be multiple hot-toasts opened with same id.
Example
No
duration number Duration in milliseconds after which hot-toast will be auto closed. Can be disabled via autoClose: false
Default: 3000, error = 4000, loading = 30000
Yes
autoClose boolean Auto close hot-toast after duration
Default: true
Yes
position ToastPosition The position to place the hot-toast.
Default: top-center
Example
Yes
dismissible boolean Show close button in hot-toast
Default: false
Example
Yes
role ToastRole Role of the live region.
Default: status
Yes
ariaLive ToastAriaLive aria-live value for the live region.
Default: polite
Yes
theme ToastTheme β€” 'toast' | 'snackbar' | 'material' | 'minimal' | 'glassmorphism' | 'ios' Visual theme of the toast. The toast and snackbar values are included in the base styles; the others each require their own stylesheet import.
Default: toast
Example
Yes
persist {ToastPersistConfig} Useful when you want to keep a persistance for toast based on ids, across sessions.
Example
No
icon Content Icon to show in the hot-toast
Example
Yes
iconTheme IconTheme Use this to change icon color
Example
Yes
className string Extra CSS classes to be added to the hot toast container. Yes
attributes Record<string, string> Extra attributes to be added to the hot toast container. Can be used for e2e tests. Yes
style style object Extra styles to apply for hot-toast.
Example
Yes
closeStyle style object Extra styles to apply for close button Yes
data DataType Allows you to pass data for your template and component. You can access the data using toastRef.data.
Examples: Template with Data, Component with Data
No
injector Injector Allows you to pass injector for your component.
Example
No
group group Allows you to set group options.
Examples: Pre-Grouping, Post-Grouping
No

Injection Tokens

HOT_TOAST_CONTAINER_TOKEN

Injection token to provide a custom container selector for the toast container. The value will be used as a selector to find the container element using document.querySelector.

import { HOT_TOAST_CONTAINER_TOKEN, provideHotToastConfig } from '@ngxpert/hot-toast';

bootstrapApplication(AppComponent, {
  providers: [
    provideHotToastConfig(),
    {
      provide: HOT_TOAST_CONTAINER_TOKEN,
      useValue: '#toast-container',
    },
  ],
}).catch((err) => console.error(err));

HOT_TOAST_HTTP_INTERCEPTOR_CONFIG

Optional injection token used by hotToastHttpInterceptor. Prefer provideHotToastHttpInterceptor(partialConfig) at bootstrap; supply this token directly only if you need a custom provider pattern.


Supported Browsers

Latest versions of Chrome, Edge, Firefox and Safari are supported, with some known issues.

Accessibility

Hot-toast messages are announced via an aria-live region. By default, the polite setting is used. While polite is recommended, this can be customized by setting the ariaLive property of the ToastConfig or ToastOptions.

Focus is not, and should not be, moved to the hot-toast element. Moving the focus would be disruptive to a user in the middle of a workflow. It is recommended that, for any action offered in the hot-toast, the application offers the user an alternative way to perform the action. Alternative interactions are typically keyboard shortcuts or menu options. When the action is performed in this way, the hot-toast should be dismissed.

Hot-toasts that have an action available should be set autoClose: false, as to accommodate screen-reader users that want to navigate to the hot-toast element to activate the action.

Breaking Changes

v1 to v2

The <div> surrounding <ng-container> is removed from .hot-toast-message to better and easy structure of layout. User may need to check their templates after updating to v2.

v2 to v3

None

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Dharmen Shah
Dharmen Shah

πŸ’» πŸ–‹ 🎨 πŸ“– πŸ’‘
Netanel Basal
Netanel Basal

πŸ› πŸ’Ό πŸ€” 🚧 πŸ§‘β€πŸ« πŸ“† πŸ”¬ πŸ‘€
Timo Lins
Timo Lins

🎨 πŸ€”
Patrick Miller
Patrick Miller

🚧 πŸ“¦
Gili Yaniv
Gili Yaniv

πŸ’»
Artur Androsovych
Artur Androsovych

🚧
Luis Castro
Luis Castro

πŸ’»

This project follows the all-contributors specification. Contributions of any kind welcome!

Icons made by Freepik from www.flaticon.com