sonner

An opinionated toast component for Angular.

PreviousNext
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardSonnerService } from '@/shared/components/sonner/sonner.service';

@Component({
  selector: 'z-demo-sonner-default',
  imports: [ZardButtonComponent],
  template: `
    <button type="button" z-button zType="outline" (click)="show()">Show Toast</button>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSonnerDefaultComponent {
  private readonly sonner = inject(ZardSonnerService);

  show() {
    this.sonner.show('Event has been created', {
      description: 'Sunday, December 03, 2023 at 9:00 AM',
      action: {
        label: 'Undo',
        onClick: () => console.log('Undo'),
      },
    });
  }
}

Installation

1

Run the CLI

Use the CLI to add sonner to your project.

Copy
npx zard-cli@latest add sonner
2

Register sonner to your project

app.component.ts
Copy
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { ZardSonnerComponent } from '@/shared/components/sonner/sonner.component';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, ZardSonnerComponent],
  template: `
    <router-outlet></router-outlet>
    <z-sonner />
  `,
})
export class AppComponent {}

Usage

import { ZardSonnerComponent } from '@/shared/components/sonner/sonner.component';
Copy
<z-sonner />
Copy

Examples

types

import { ChangeDetectionStrategy, Component, inject } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardSonnerService } from '@/shared/components/sonner/sonner.service';

@Component({
  selector: 'z-demo-sonner-types',
  imports: [ZardButtonComponent],
  template: `
    <div class="flex flex-wrap gap-2">
      <button type="button" z-button zType="outline" (click)="sonner.show('Event has been created')">Default</button>
      <button type="button" z-button zType="outline" (click)="sonner.success('Event has been created')">Success</button>
      <button
        type="button"
        z-button
        zType="outline"
        (click)="sonner.info('Be at the area 10 minutes before the event time')"
      >
        Info
      </button>
      <button
        type="button"
        z-button
        zType="outline"
        (click)="sonner.warning('Event start time cannot be earlier than 8am')"
      >
        Warning
      </button>
      <button type="button" z-button zType="outline" (click)="sonner.error('Event has not been created')">Error</button>
      <button type="button" z-button zType="outline" (click)="showPromise()">Promise</button>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSonnerTypesComponent {
  protected readonly sonner = inject(ZardSonnerService);

  showPromise() {
    this.sonner.promise<{ name: string }>(
      () => new Promise(resolve => setTimeout(() => resolve({ name: 'Event' }), 2000)),
      {
        loading: 'Loading...',
        success: data => `${data.name} has been created`,
        error: 'Error',
      },
    );
  }
}

description

import { ChangeDetectionStrategy, Component, inject } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardSonnerService } from '@/shared/components/sonner/sonner.service';

@Component({
  selector: 'z-demo-sonner-description',
  imports: [ZardButtonComponent],
  template: `
    <button type="button" z-button zType="outline" class="w-fit" (click)="show()">Show Toast</button>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSonnerDescriptionComponent {
  private readonly sonner = inject(ZardSonnerService);

  show() {
    this.sonner.show('Event has been created', {
      description: 'Monday, January 3rd at 6:00pm',
    });
  }
}

position

Use the position option to change the position of the toast.
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { type ZardSonnerPosition } from '@/shared/components/sonner/sonner.component';
import { ZardSonnerService } from '@/shared/components/sonner/sonner.service';

@Component({
  selector: 'z-demo-sonner-position',
  imports: [ZardButtonComponent],
  template: `
    <div class="flex flex-wrap justify-center gap-2">
      <button type="button" z-button zType="outline" (click)="show('top-left')">Top Left</button>
      <button type="button" z-button zType="outline" (click)="show('top-center')">Top Center</button>
      <button type="button" z-button zType="outline" (click)="show('top-right')">Top Right</button>
      <button type="button" z-button zType="outline" (click)="show('bottom-left')">Bottom Left</button>
      <button type="button" z-button zType="outline" (click)="show('bottom-center')">Bottom Center</button>
      <button type="button" z-button zType="outline" (click)="show('bottom-right')">Bottom Right</button>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSonnerPositionComponent {
  private readonly sonner = inject(ZardSonnerService);

  show(position: ZardSonnerPosition) {
    this.sonner.show('Event has been created', { position });
  }
}

with dialog

Toasts are rendered in the top layer, so they stay above dialogs, drawers and sheets.
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';

import { ZardDialogImports } from '@/shared/components/dialog/dialog.imports';
import { ZardDialogService } from '@/shared/components/dialog/dialog.service';
import { ZardSonnerService } from '@/shared/components/sonner/sonner.service';

@Component({
  selector: 'z-demo-sonner-with-dialog',
  imports: [ZardDialogImports],
  template: `
    <button type="button" z-button zType="outline" (click)="openDialog()">Open dialog</button>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSonnerWithDialogComponent {
  private readonly dialogService = inject(ZardDialogService);
  private readonly sonner = inject(ZardSonnerService);

  openDialog() {
    this.dialogService.create({
      zTitle: 'Save changes',
      zDescription: 'The toast stays above the dialog and its backdrop.',
      zContent: 'Confirm to dispatch a toast without closing the dialog.',
      zOkText: 'Save',
      zMaskClosable: false,
      zOnOk: () => {
        this.sonner.success('Changes saved');
        return false;
      },
    });
  }
}

API Reference

z-sonnerComponent

Container that renders toast notifications. Place once at the root of your app.

PropertyDescriptionTypeDefault
[style] Inline styles applied to every toast Record<string, string> undefined
[theme] Theme used for the toasts. 'light' | 'dark' | 'system' 'system'
[position] Position of the toast container on the viewport. 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' 'top-center'
[richColors] Enables tinted backgrounds for success / error / warning / info toasts. boolean false
[expand] Expands toasts by default instead of stacking them. boolean false
[duration] Default auto-dismiss duration (ms). number 4000
[visibleToasts] Maximum number of toasts visible at the same time. number 3
[closeButton] Shows a close button on each toast. boolean false
[toastOptions] Default options applied to every toast (classes, duration, descriptions, etc). ToasterProps['toastOptions'] {}
[dir] Text direction for toasts. 'ltr' | 'rtl' | 'auto' 'auto'
[topLayer] Renders the toaster in the native top layer so toasts stay above dialogs, drawers and sheets. Disable it only if the app opts out of the CDK top layer. boolean true
[class] Additional Tailwind / utility classes merged into the host. ClassValue ''

ZardSonnerServiceComponent

Inject this service to dispatch toasts from any component or service.

PropertyDescriptionTypeDefault
show(message, options?) Dispatches a default toast and returns its id. (message: string | number, options?: ExternalToast) => string | number
success(message, options?) Dispatches a success-styled toast (green). (message: string | number, options?: ExternalToast) => string | number
error(message, options?) Dispatches an error-styled toast (red). (message: string | number, options?: ExternalToast) => string | number
warning(message, options?) Dispatches a warning-styled toast (yellow). (message: string | number, options?: ExternalToast) => string | number
info(message, options?) Dispatches an info-styled toast (blue). (message: string | number, options?: ExternalToast) => string | number
loading(message, options?) Dispatches a loading-styled toast with a spinner. (message: string | number, options?: ExternalToast) => string | number
message(message, options?) Dispatches a neutral message-styled toast. (message: string | number, options?: ExternalToast) => string | number
promise(promise, options) Binds a toast to the lifecycle of a promise (loading → success/error). <T>(promise: PromiseT<T>, options: PromiseData<T>) => string | number
dismiss(id?) Dismisses a toast by id, or all toasts when no id is provided. (id?: string | number) => void
github iconwhatsapp icondiscord iconX icon

Made with in Brazil. Open source and available on GitHub .