notify-zh

Toast notifications in ~2.7 KB zero dependencies

Promise APITypeScriptSSR-safeAny framework
import notify from 'notify-zh'
GitHubnpm

Interactive Playground

Customize your notification in real-time

Content

Customization

Style Customization

Live Preview

Hello from notify-zh!

Get Started in Seconds

Choose your preferred installation method and start building amazing user experiences.

📦 NPM

Perfect for modern build tools and bundlers

npm install notify-zh

🌐 CDN

Quick setup for prototypes and simple projects

<script src="https://unpkg.com/notify-zh"></script>

Simple & Powerful API

Get up and running with just a few lines of code. No complex configuration needed.

<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/notify-zh"></script>
</head>
<body>
  <script>
    // window.notify is ready to use
    notify.success({ message: 'Operation successful!' });
    notify.error({ message: 'Something went wrong' });
    notify.warning({ message: 'Be careful' });
    notify.info({ message: 'Important information' });

    // With custom options
    notify.success({
      message: 'Custom message',
      time: 5000,
      position: 'top-right',
      closable: true
    });
  </script>
</body>
</html>
---
// src/pages/index.astro
---

<html>
<head>
  <title>Astro + notify-zh</title>
</head>
<body>
  <button id="notify-btn">Show Notification</button>
  
  <script>
    import notify from 'notify-zh';

    document.getElementById('notify-btn').addEventListener('click', () => {
      notify.success({
        message: 'Hello from Astro!',
        position: 'top-right',
        time: 3000
      });
    });
  </script>
</body>
</html>
import React from 'react';
import notify from 'notify-zh';

function App() {
  const handleClick = () => {
    notify.success({ message: 'Hello from React!' });
  };

  const showError = () => {
    notify.error({
      message: 'Something went wrong!',
      time: 5000,
      position: 'bottom-right'
    });
  };

  // notify.promise(): loading → success/error automatically
  const saveData = () => {
    notify.promise(fetch('/api/save'), {
      loading: 'Saving…',
      success: 'Saved!',
      error: 'Failed to save'
    });
  };

  return (
    <div>
      <button onClick={handleClick}>Success</button>
      <button onClick={showError}>Error</button>
      <button onClick={saveData}>Save (promise)</button>
    </div>
  );
}

export default App;
<template>
  <div>
    <button @click="showSuccess">Success</button>
    <button @click="showError">Error</button>
    <button @click="showWarning">Warning</button>
    <button @click="showInfo">Info</button>
  </div>
</template>

<script>
import notify from 'notify-zh';

export default {
  name: 'NotificationDemo',
  methods: {
    showSuccess() {
      notify.success({ message: 'Operation completed successfully!' });
    },
    showError() {
      notify.error({ message: 'An error occurred!' });
    },
    showWarning() {
      notify.warning({ message: 'Please be careful!' });
    },
    showInfo() {
      notify.info({
        message: 'Here is some information',
        position: 'top-left',
        time: 4000
      });
    }
  }
};
</script>
// app/page.js (App Router)
'use client';

import notify from 'notify-zh';

export default function Home() {
  // SSR-safe: calls are no-ops on the server, no window guards needed
  const handleSubmit = (e) => {
    e.preventDefault();

    notify.promise(fetch('/api/submit'), {
      loading: 'Submitting…',
      success: 'Form submitted successfully!',
      error: 'Failed to submit form'
    });
  };

  return (
    <div>
      <h1>Next.js + notify-zh</h1>
      <form onSubmit={handleSubmit}>
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}
// app.component.ts
import { Component } from '@angular/core';
import notify from 'notify-zh';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <h1>Angular + notify-zh</h1>
      <button (click)="showSuccess()">Success</button>
      <button (click)="showError()">Error</button>
      <button (click)="showWarning()">Warning</button>
      <button (click)="showInfo()">Info</button>
    </div>
  `
})
export class AppComponent {
  title = 'angular-notify-zh';

  showSuccess() {
    notify.success({ message: 'Task completed successfully!' });
  }

  showError() {
    notify.error({ message: 'An error has occurred!' });
  }

  showWarning() {
    notify.warning({ message: 'Warning: Please check your input!' });
  }

  showInfo() {
    notify.info({
      message: 'Information: Process started',
      time: 6000,
      position: 'center-bottom'
    });
  }
}

Why Choose notify-zh?

See how we stack up against the most popular notification libraries

LighterMore FlexibleModern
Feature
notify-zh
Toastify
Notyf
SweetAlert2
Bundle Size
~2.7KB🏆 Winner
~8KB
~4KB
~45KB
Zero Dependencies
Native TypeScript
Flexible Positions
7 positions🏆 Most Options
4 positions
4 positions
3 positions
Custom Icons
✅ HTML/SVG🏆 Complete
✅ Limited
Tailwind CSS
✅ Integrated🏆 Unique
Simple API
❌ Complex
Accessibility
✅ ARIA

🚀 Performance

At just ~2.7KB, notify-zh is one of the lightest libraries on the market without sacrificing functionality.

🎨 Flexibility

Native Tailwind CSS support and 7 different positions for maximum customization.

⚡ Simplicity

Intuitive and easy-to-use API, perfect for projects of any size.

Frequently asked questions

Straight answers about SSR, styling, security and CSP — the same FAQ that ships in the README.

The toast doesn't appear in Next.js/Nuxt — why?

Since v1.1.0 all calls are SSR-safe no-ops on the server, so nothing crashes — but a toast fired during server render never shows. Fire notifications from client-side events (clicks, effects), and in the App Router use 'use client' components.

Can a notification stay until the user closes it?

Yes: notify.warning({ message: '…', time: Infinity, closable: true }).

How do I use Tailwind or Bootstrap classes?

Set disableDefaultStyles: true and map your classes via classNames in notify.config(). When a per-type class is set, the library skips its inline background so your class always wins.

Is icon.el safe?

message and title are always rendered as plain text (XSS-safe). Only icon.el is injected as HTML so you can pass inline SVG — never pass user-generated content to it.

Does it work with a strict CSP (no inline styles)?

The default styles are injected as a <style> tag, which requires style-src to allow it. With a strict CSP, set disableDefaultStyles: true and style toasts with your own stylesheet classes via classNames.

Why doesn't the toast auto-close while I hover it?

That's pauseOnHover (on by default) — the timer resumes when the pointer leaves. Disable with notify.config({ pauseOnHover: false }).

Support the project

notify-zh is free and MIT-licensed. If it saved you a dependency, a star or a coffee keeps it moving.

You can also contribute code — issues and PRs welcome onGitHub.