A lightweight, framework-agnostic notification library with zero dependencies
Demo β’ Installation β’ Quick Start β’ Examples β’ API
NotifyX is designed for developers who want beautiful, accessible toast notifications without the bloat. Here's what makes it special:
- π― Universal - Works seamlessly with React, Next.js, Vue, Angular, Svelte, Laravel, or plain JavaScript
- β‘ Tiny Bundle - Less than 3KB gzipped with zero runtime dependencies
- π¨ Beautiful by Default - Gorgeous Tailwind CSS styles with smooth animations
- π Dark Mode - Automatic dark mode detection and beautiful theming
- π TypeScript First - Built with TypeScript, includes complete type definitions
- βΏ Accessible - WCAG compliant with ARIA attributes and keyboard support
- ποΈ Flexible - Four toast types, four positions, customizable duration, and easy styling
- π Production Ready - Battle-tested with comprehensive error handling
# npm
npm install notifyx
# yarn
yarn add notifyx
# pnpm
pnpm add notifyx
# bun
bun add notifyxChoose your favorite package manager:
# npm
npm install notifyx
# yarn
yarn add notifyx
# pnpm
pnpm add notifyx
# bun
bun add notifyxOr use via CDN (for vanilla JavaScript projects):
<!-- CSS -->
<link rel="stylesheet" href="https://unpkg.com/notifyx@latest/dist/notifyx.min.css" />
<!-- JavaScript -->
<script src="https://unpkg.com/notifyx@latest/dist/notifyx.min.js"></script>Step 1: Import NotifyX and its styles
import NotifyX from 'notifyx';
import 'notifyx/style.css';Step 2: Show your first notification
NotifyX.success('Welcome to NotifyX! π');That's it! You now have beautiful toast notifications.
NotifyX provides four notification types for different scenarios:
// β
Success - For positive outcomes
NotifyX.success('Payment completed successfully!');
// β Error - For errors and failures
NotifyX.error('Failed to upload file. Please try again.');
// β οΈ Warning - For important alerts
NotifyX.warning('Your session will expire in 5 minutes.');
// βΉοΈ Info - For general information
NotifyX.info('New features are now available!');| Type | When to Use | Example |
|---|---|---|
| Success | Successful operations, confirmations | "Settings saved", "File uploaded" |
| Error | Errors, failures, validation issues | "Network error", "Invalid input" |
| Warning | Important warnings, cautions | "Low storage", "Unsaved changes" |
| Info | General information, updates | "New message", "System update" |
Place notifications in any corner of the screen:
// Top positions (default: top-right)
NotifyX.info('Top Right Position', { position: 'top-right' });
NotifyX.info('Top Left Position', { position: 'top-left' });
// Bottom positions
NotifyX.info('Bottom Right Position', { position: 'bottom-right' });
NotifyX.info('Bottom Left Position', { position: 'bottom-left' });Pro Tip: Use constants for better code maintainability:
import NotifyX, { POSITIONS } from 'notifyx';
NotifyX.success('Saved!', { position: POSITIONS.BOTTOM_RIGHT });
NotifyX.error('Error!', { position: POSITIONS.TOP_LEFT });Customize how long notifications stay visible:
// Quick notification (1 second)
NotifyX.success('Copied!', { duration: 1000 });
// Standard notification (3 seconds - default)
NotifyX.info('Processing your request...');
// Longer notification (10 seconds)
NotifyX.warning('Please read this carefully!', { duration: 10000 });
// Persistent notification (stays until manually dismissed)
NotifyX.error('Critical error - action required', { duration: 0 });Duration Quick Reference:
duration: 1000- 1 second (quick actions)duration: 3000- 3 seconds (default)duration: 5000- 5 seconds (important info)duration: 0- Persistent (manual dismiss only)
For complete control, use the show() method with custom options:
NotifyX.show({
message: 'User profile updated successfully!',
type: 'success',
position: 'bottom-right',
duration: 5000,
dismissible: true,
onClose: () => {
console.log('Notification was closed');
// Perform cleanup or tracking
}
});{
message: string; // The notification message (required)
type: string; // 'success' | 'error' | 'warning' | 'info'
position: string; // 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
duration: number; // Milliseconds (0 = persistent)
dismissible: boolean; // Show close button (default: true)
onClose: () => void; // Callback when notification closes
maxToasts: number; // Maximum simultaneous toasts (default: 5)
}function handleFormSubmit(event) {
event.preventDefault();
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
// Validate email
if (!email || !email.includes('@')) {
NotifyX.error('Please enter a valid email address');
return;
}
// Validate password
if (!password || password.length < 8) {
NotifyX.error('Password must be at least 8 characters');
return;
}
// Success
NotifyX.success('Account created successfully! π');
}async function fetchUserData(userId) {
try {
// Show loading state
NotifyX.info('Loading user data...', { duration: 0 });
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('Failed to fetch user');
}
const data = await response.json();
// Clear loading and show success
NotifyX.clear();
NotifyX.success(`Welcome back, ${data.name}!`);
return data;
} catch (error) {
NotifyX.clear();
NotifyX.error('Unable to load user data. Please try again.');
console.error(error);
}
}function copyToClipboard(text) {
navigator.clipboard.writeText(text)
.then(() => {
NotifyX.success('Copied to clipboard!', {
duration: 2000,
position: 'bottom-right'
});
})
.catch(() => {
NotifyX.error('Failed to copy. Please try again.');
});
}
// Usage
document.querySelector('#copy-btn').addEventListener('click', () => {
copyToClipboard('Hello, NotifyX!');
});async function uploadFile(file) {
// Show initial notification
NotifyX.info('Uploading file...', { duration: 0 });
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
if (response.ok) {
NotifyX.clear();
NotifyX.success(`${file.name} uploaded successfully!`);
} else {
throw new Error('Upload failed');
}
} catch (error) {
NotifyX.clear();
NotifyX.error('Upload failed. Please try again.');
}
}function deleteItem(itemId, itemName) {
if (confirm(`Are you sure you want to delete "${itemName}"?`)) {
NotifyX.info('Deleting...', { duration: 0 });
fetch(`/api/items/${itemId}`, { method: 'DELETE' })
.then(() => {
NotifyX.clear();
NotifyX.success('Item deleted successfully', {
duration: 3000,
onClose: () => {
// Refresh the list after notification closes
refreshItemList();
}
});
})
.catch(() => {
NotifyX.clear();
NotifyX.error('Failed to delete item');
});
}
}function performBatchOperation(items) {
let successCount = 0;
let errorCount = 0;
items.forEach(async (item, index) => {
try {
await processItem(item);
successCount++;
// Show progress
NotifyX.info(`Processing: ${index + 1} of ${items.length}`);
} catch (error) {
errorCount++;
}
});
// Show final summary
setTimeout(() => {
NotifyX.clear(); // Clear progress notifications
if (errorCount === 0) {
NotifyX.success(`All ${successCount} items processed successfully!`);
} else {
NotifyX.warning(`Completed with ${successCount} success, ${errorCount} errors`);
}
}, 2000);
}import React from 'react';
import NotifyX from 'notifyx';
import 'notifyx/style.css';
function App() {
const handleClick = () => {
NotifyX.success('Button clicked!');
};
return (
<div>
<button onClick={handleClick}>Show Notification</button>
</div>
);
}
export default App;import { useCallback } from 'react';
import NotifyX from 'notifyx';
import 'notifyx/style.css';
// Custom hook for notifications
function useNotification() {
const showSuccess = useCallback((message) => {
NotifyX.success(message);
}, []);
const showError = useCallback((message) => {
NotifyX.error(message);
}, []);
const showWarning = useCallback((message) => {
NotifyX.warning(message);
}, []);
const showInfo = useCallback((message) => {
NotifyX.info(message);
}, []);
return { showSuccess, showError, showWarning, showInfo };
}
// Usage in component
function MyComponent() {
const notify = useNotification();
const handleSave = () => {
notify.showSuccess('Changes saved!');
};
return <button onClick={handleSave}>Save</button>;
}Step 1: Create a client component for notifications
// app/components/ToastButton.tsx
'use client';
import NotifyX from 'notifyx';
import 'notifyx/style.css';
export default function ToastButton() {
return (
<button
onClick={() => NotifyX.success('Hello from Next.js!')}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
Show Notification
</button>
);
}Step 2: Import styles in your root layout
// app/layout.tsx
import 'notifyx/style.css';
import './globals.css';
export default function RootLayout({
children
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}Step 3: Use the component in your pages
// app/page.tsx
import ToastButton from './components/ToastButton';
export default function Home() {
return (
<main>
<h1>My Next.js App</h1>
<ToastButton />
</main>
);
}// pages/_app.tsx
import type { AppProps } from 'next/app';
import 'notifyx/style.css';
import '../styles/globals.css';
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}// pages/index.tsx
import NotifyX from 'notifyx';
export default function Home() {
return (
<button onClick={() => NotifyX.success('Next.js Pages Router!')}>
Show Toast
</button>
);
}// app/api/save/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
try {
const data = await request.json();
// Process data...
return NextResponse.json({
success: true,
message: 'Data saved successfully!'
});
} catch (error) {
return NextResponse.json({
success: false,
message: 'Failed to save data'
}, { status: 500 });
}
}// Client component using the API
'use client';
import NotifyX from 'notifyx';
export default function SaveButton() {
const handleSave = async () => {
try {
const response = await fetch('/api/save', {
method: 'POST',
body: JSON.stringify({ data: 'example' })
});
const result = await response.json();
if (result.success) {
NotifyX.success(result.message);
} else {
NotifyX.error(result.message);
}
} catch (error) {
NotifyX.error('Network error occurred');
}
};
return <button onClick={handleSave}>Save Data</button>;
}<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 setup>
import NotifyX from 'notifyx';
import 'notifyx/style.css';
const showSuccess = () => {
NotifyX.success('Operation successful!');
};
const showError = () => {
NotifyX.error('Something went wrong!');
};
const showWarning = () => {
NotifyX.warning('Please be careful!');
};
const showInfo = () => {
NotifyX.info('Here is some information.');
};
</script>// composables/useNotify.ts
import NotifyX from 'notifyx';
export function useNotify() {
const success = (message: string) => NotifyX.success(message);
const error = (message: string) => NotifyX.error(message);
const warning = (message: string) => NotifyX.warning(message);
const info = (message: string) => NotifyX.info(message);
return { success, error, warning, info };
}<template>
<button @click="notify.success('Saved successfully!')">Save</button>
</template>
<script setup>
import { useNotify } from '@/composables/useNotify';
const notify = useNotify();
</script>// app.component.ts
import { Component } from '@angular/core';
import NotifyX from 'notifyx';
@Component({
selector: 'app-root',
template: `
<button (click)="showNotification()">Show Toast</button>
`,
styles: []
})
export class AppComponent {
showNotification() {
NotifyX.success('Hello from Angular!');
}
}{
"styles": [
"src/styles.css",
"node_modules/notifyx/style.css"
]
}// services/notification.service.ts
import { Injectable } from '@angular/core';
import NotifyX from 'notifyx';
@Injectable({
providedIn: 'root'
})
export class NotificationService {
success(message: string) {
NotifyX.success(message);
}
error(message: string) {
NotifyX.error(message);
}
warning(message: string) {
NotifyX.warning(message);
}
info(message: string) {
NotifyX.info(message);
}
clear() {
NotifyX.clear();
}
}// app.component.ts
import { Component } from '@angular/core';
import { NotificationService } from './services/notification.service';
@Component({
selector: 'app-root',
template: `<button (click)="save()">Save</button>`
})
export class AppComponent {
constructor(private notification: NotificationService) {}
save() {
this.notification.success('Saved successfully!');
}
}Step 1: Install NotifyX
npm install notifyxStep 2: Import in your main JavaScript file
// resources/js/app.js
import NotifyX from 'notifyx';
import 'notifyx/style.css';
// Make NotifyX globally available
window.NotifyX = NotifyX;
// Example helper function
window.showToast = (message, type = 'info') => {
NotifyX[type](message);
};Step 3: Use in Blade templates
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My Laravel App</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
@yield('content')
{{-- Flash messages --}}
@if(session('success'))
<script>
document.addEventListener('DOMContentLoaded', () => {
NotifyX.success('{{ session('success') }}');
});
</script>
@endif
@if(session('error'))
<script>
document.addEventListener('DOMContentLoaded', () => {
NotifyX.error('{{ session('error') }}');
});
</script>
@endif
</body>
</html>{{-- resources/views/posts/create.blade.php --}}
@extends('layouts.app')
@section('content')
<form action="/posts" method="POST" onsubmit="handleSubmit(event)">
@csrf
<input type="text" name="title" required>
<button type="submit">Create Post</button>
</form>
<script>
function handleSubmit(event) {
// Client-side notification
showToast('Creating post...', 'info');
}
</script>
@endsection{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My Laravel App</title>
<link rel="stylesheet" href="https://unpkg.com/notifyx@latest/dist/notifyx.min.css">
</head>
<body>
@yield('content')
<script src="https://unpkg.com/notifyx@latest/dist/notifyx.min.js"></script>
{{-- Laravel flash messages --}}
@if(session('success'))
<script>NotifyX.success('{{ session('success') }}');</script>
@endif
@if(session('error'))
<script>NotifyX.error('{{ session('error') }}');</script>
@endif
</body>
</html>// app/Http/Controllers/PostController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function store(Request $request)
{
$request->validate([
'title' => 'required|max:255',
'content' => 'required',
]);
// Save post...
return redirect('/posts')
->with('success', 'Post created successfully!');
}
public function destroy($id)
{
try {
// Delete post...
return redirect('/posts')
->with('success', 'Post deleted successfully!');
} catch (\Exception $e) {
return redirect('/posts')
->with('error', 'Failed to delete post.');
}
}
}Perfect for static sites, WordPress, or any HTML page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NotifyX Demo</title>
<!-- NotifyX CSS -->
<link rel="stylesheet" href="https://unpkg.com/notifyx@latest/dist/notifyx.min.css">
</head>
<body>
<h1>NotifyX Vanilla JS Demo</h1>
<button onclick="NotifyX.success('Success!')">Success</button>
<button onclick="NotifyX.error('Error!')">Error</button>
<button onclick="NotifyX.warning('Warning!')">Warning</button>
<button onclick="NotifyX.info('Info!')">Info</button>
<button onclick="NotifyX.clear()">Clear All</button>
<!-- NotifyX JavaScript -->
<script src="https://unpkg.com/notifyx@latest/dist/notifyx.min.js"></script>
<script>
// Show welcome message on page load
window.addEventListener('DOMContentLoaded', () => {
NotifyX.info('Welcome! Click any button to see notifications.', {
duration: 5000,
position: 'top-right'
});
});
// Custom function with configuration
function showCustomToast() {
NotifyX.show({
message: 'Custom notification with all options!',
type: 'success',
position: 'bottom-right',
duration: 4000,
dismissible: true,
onClose: () => console.log('Toast closed!')
});
}
</script>
</body>
</html>Display a notification with full configuration options.
Parameters:
options(object) - Configuration object
Example:
NotifyX.show({
message: 'Profile updated!',
type: 'success',
position: 'bottom-right',
duration: 5000,
dismissible: true,
onClose: () => console.log('Closed'),
maxToasts: 5
});Show a success notification.
Parameters:
message(string) - Notification messageoptions(object, optional) - Override default options
Example:
NotifyX.success('File uploaded successfully!');
NotifyX.success('Saved!', { duration: 2000, position: 'bottom-right' });Show an error notification.
Parameters:
message(string) - Error messageoptions(object, optional) - Override default options
Example:
NotifyX.error('Failed to connect to server');
NotifyX.error('Invalid credentials', { duration: 5000 });Show a warning notification.
Parameters:
message(string) - Warning messageoptions(object, optional) - Override default options
Example:
NotifyX.warning('Your session will expire soon');
NotifyX.warning('Unsaved changes', { duration: 0 }); // PersistentShow an info notification.
Parameters:
message(string) - Info messageoptions(object, optional) - Override default options
Example:
NotifyX.info('New features available!');
NotifyX.info('Loading...', { duration: 0 }); // Stays until clearedRemove all active notifications immediately.
Example:
NotifyX.clear(); // Removes all toastsNotifyX is built with TypeScript and exports comprehensive types:
import NotifyX, {
ToastOptions,
ToastType,
Position,
POSITIONS,
DEFAULT_OPTIONS
} from 'notifyx';
// Type-safe notification
const options: ToastOptions = {
message: 'Type-safe notification!',
type: 'success',
position: 'top-right',
duration: 3000,
dismissible: true,
onClose: () => console.log('Closed')
};
NotifyX.show(options);
// Using type unions
const notificationType: ToastType = 'error';
const notificationPosition: Position = POSITIONS.BOTTOM_LEFT;
NotifyX.show({
message: 'Using TypeScript types',
type: notificationType,
position: notificationPosition
});type ToastType = 'success' | 'error' | 'warning' | 'info';
type Position = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
interface ToastOptions {
message: string;
type?: ToastType;
duration?: number;
position?: Position;
dismissible?: boolean;
onClose?: () => void;
maxToasts?: number;
}Predefined position constants for type safety:
import { POSITIONS } from 'notifyx';
POSITIONS.TOP_RIGHT // 'top-right'
POSITIONS.TOP_LEFT // 'top-left'
POSITIONS.BOTTOM_RIGHT // 'bottom-right'
POSITIONS.BOTTOM_LEFT // 'bottom-left'Default configuration values:
import { DEFAULT_OPTIONS } from 'notifyx';
console.log(DEFAULT_OPTIONS);
// {
// type: 'info',
// duration: 3000,
// position: 'top-right',
// dismissible: true,
// maxToasts: 5
// }CSS animation class names:
import { ANIMATION_CLASSES } from 'notifyx';
ANIMATION_CLASSES.enter // 'notifyx-enter'
ANIMATION_CLASSES.exit // 'notifyx-exit'
ANIMATION_CLASSES.slideEnter // 'notifyx-slide-enter'
ANIMATION_CLASSES.slideExit // 'notifyx-slide-exit'Override default styles with your own CSS:
/* Custom toast container positioning */
.notifyx-container[data-position="top-right"] {
top: 20px;
right: 20px;
}
/* Custom toast appearance */
.notifyx {
border-radius: 12px;
padding: 16px 20px;
font-family: 'Inter', sans-serif;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
}
/* Success toast gradient */
.notifyx-success {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
}
/* Error toast gradient */
.notifyx-error {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
border: none;
}
/* Warning toast */
.notifyx-warning {
background: #fbbf24;
color: #78350f;
border: 2px solid #f59e0b;
}
/* Info toast */
.notifyx-info {
background: #3b82f6;
color: white;
border: none;
}
/* Customize close button */
.notifyx-close {
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
width: 24px;
height: 24px;
font-size: 14px;
}
.notifyx-close:hover {
background: rgba(255, 255, 255, 0.3);
transform: scale(1.1);
}
/* Dark mode custom styles */
@media (prefers-color-scheme: dark) {
.notifyx {
background: rgba(30, 30, 30, 0.95);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
}
.notifyx-success {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
}
}
/* Custom animations */
@keyframes customSlideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.notifyx-enter {
animation: customSlideIn 0.3s ease-out;
}Extend NotifyX styles with Tailwind classes:
// After showing a toast, add Tailwind classes
const toast = document.querySelector('.notifyx:last-child');
toast.classList.add('backdrop-blur-md', 'shadow-2xl', 'ring-2', 'ring-blue-500');Or create a wrapper function:
function showStyledToast(message, type = 'info') {
NotifyX[type](message);
setTimeout(() => {
const toast = document.querySelector('.notifyx:last-child');
if (toast) {
toast.classList.add('backdrop-blur-md', 'shadow-2xl');
}
}, 10);
}-
Limit Maximum Toasts: Control memory usage
NotifyX.show({ message: 'Hello', maxToasts: 3 });
-
Clear Old Notifications: Remove unnecessary toasts
NotifyX.clear(); // Clear all before showing new batch
-
Use Appropriate Durations: Don't keep toasts open unnecessarily
// Quick actions NotifyX.success('Copied!', { duration: 1000 }); // Important info NotifyX.warning('Read carefully', { duration: 5000 });
-
Batch Operations: Clear between batches
function processBatch() { NotifyX.clear(); // Clear previous NotifyX.info('Processing batch...'); }
Problem: Notifications appear but have no styles.
Solution: Make sure you've imported the CSS:
import 'notifyx/style.css';For CDN:
<link rel="stylesheet" href="https://unpkg.com/notifyx@latest/dist/notifyx.min.css">Problem: ReferenceError: NotifyX is not defined
Solution: Ensure the script is loaded before use:
<script src="https://unpkg.com/notifyx@latest/dist/notifyx.min.js"></script>
<script>
// Now you can use NotifyX
NotifyX.success('Works!');
</script>Problem: Error during server-side rendering.
Solution: Use client components:
'use client'; // Add this at the top
import NotifyX from 'notifyx';Problem: Multiple toasts appear on re-renders.
Solution: Use useCallback or useMemo:
const showToast = useCallback(() => {
NotifyX.success('Success!');
}, []);NotifyX works on all modern browsers:
| Browser | Minimum Version |
|---|---|
| Chrome | 60+ |
| Firefox | 55+ |
| Safari | 12+ |
| Edge | 79+ |
| Opera | 47+ |
| Samsung Internet | 8+ |
Mobile Support: β iOS Safari 12+, Chrome Mobile, Firefox Mobile
We welcome contributions! Here's how you can help:
- Report Bugs: Open an issue
- Suggest Features: Start a discussion
- Submit PRs: Fork, create a branch, and submit a pull request
- Improve Docs: Help us make this README even better!
# Clone the repository
git clone https://github.com/awalhadi/notifyx.git
cd notifyx
# Install dependencies
npm install
# Build the project
npm run build
# Run development server
npm run devMIT License - feel free to use NotifyX in your personal and commercial projects!
Copyright (c) 2025 A Awal Hadi
- Built with β€οΈ using TypeScript
- Styled with Tailwind CSS
- Bundled with Vite
- Inspired by modern notification libraries
- GitHub: github.com/awalhadi/notifyx
- Issues: Report a bug
- npm: npmjs.com/package/notifyx
Made with β€οΈ by A Awal Hadi
If NotifyX helped your project, consider giving it a β on GitHub!