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 --><linkrel="stylesheet" href="https://unpkg.com/notifyx@latest/dist/notifyx.min.css" /><!-- JavaScript --><scriptsrc="https://unpkg.com/notifyx@latest/dist/notifyx.min.js"></script>Step 1: Import NotifyX and its styles
importNotifyXfrom'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 outcomesNotifyX.success('Payment completed successfully!');// ❌ Error - For errors and failuresNotifyX.error('Failed to upload file. Please try again.');// ⚠️ Warning - For important alertsNotifyX.warning('Your session will expire in 5 minutes.');// ℹ️ Info - For general informationNotifyX.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 positionsNotifyX.info('Bottom Right Position',{position: 'bottom-right'});NotifyX.info('Bottom Left Position',{position: 'bottom-left'});Pro Tip: Use constants for better code maintainability:
importNotifyX,{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)}functionhandleFormSubmit(event){event.preventDefault();constemail=document.getElementById('email').value;constpassword=document.getElementById('password').value;// Validate emailif(!email||!email.includes('@')){NotifyX.error('Please enter a valid email address');return;}// Validate passwordif(!password||password.length<8){NotifyX.error('Password must be at least 8 characters');return;}// SuccessNotifyX.success('Account created successfully! 🎉');}asyncfunctionfetchUserData(userId){try{// Show loading stateNotifyX.info('Loading user data...',{duration: 0});constresponse=awaitfetch(`/api/users/${userId}`);if(!response.ok){thrownewError('Failed to fetch user');}constdata=awaitresponse.json();// Clear loading and show successNotifyX.clear();NotifyX.success(`Welcome back, ${data.name}!`);returndata;}catch(error){NotifyX.clear();NotifyX.error('Unable to load user data. Please try again.');console.error(error);}}functioncopyToClipboard(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.');});}// Usagedocument.querySelector('#copy-btn').addEventListener('click',()=>{copyToClipboard('Hello, NotifyX!');});asyncfunctionuploadFile(file){// Show initial notificationNotifyX.info('Uploading file...',{duration: 0});constformData=newFormData();formData.append('file',file);try{constresponse=awaitfetch('/api/upload',{method: 'POST',body: formData});if(response.ok){NotifyX.clear();NotifyX.success(`${file.name} uploaded successfully!`);}else{thrownewError('Upload failed');}}catch(error){NotifyX.clear();NotifyX.error('Upload failed. Please try again.');}}functiondeleteItem(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 closesrefreshItemList();}});}).catch(()=>{NotifyX.clear();NotifyX.error('Failed to delete item');});}}functionperformBatchOperation(items){letsuccessCount=0;leterrorCount=0;items.forEach(async(item,index)=>{try{awaitprocessItem(item);successCount++;// Show progressNotifyX.info(`Processing: ${index+1} of ${items.length}`);}catch(error){errorCount++;}});// Show final summarysetTimeout(()=>{NotifyX.clear();// Clear progress notificationsif(errorCount===0){NotifyX.success(`All ${successCount} items processed successfully!`);}else{NotifyX.warning(`Completed with ${successCount} success, ${errorCount} errors`);}},2000);}importReactfrom'react';importNotifyXfrom'notifyx';import'notifyx/style.css';functionApp(){consthandleClick=()=>{NotifyX.success('Button clicked!');};return(<div><buttononClick={handleClick}>Show Notification</button></div>);}exportdefaultApp;import{useCallback}from'react';importNotifyXfrom'notifyx';import'notifyx/style.css';// Custom hook for notificationsfunctionuseNotification(){constshowSuccess=useCallback((message)=>{NotifyX.success(message);},[]);constshowError=useCallback((message)=>{NotifyX.error(message);},[]);constshowWarning=useCallback((message)=>{NotifyX.warning(message);},[]);constshowInfo=useCallback((message)=>{NotifyX.info(message);},[]);return{ showSuccess, showError, showWarning, showInfo };}// Usage in componentfunctionMyComponent(){constnotify=useNotification();consthandleSave=()=>{notify.showSuccess('Changes saved!');};return<buttononClick={handleSave}>Save</button>;}Step 1: Create a client component for notifications
// app/components/ToastButton.tsx'use client';importNotifyXfrom'notifyx';import'notifyx/style.css';exportdefaultfunctionToastButton(){return(<buttononClick={()=>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.tsximport'notifyx/style.css';import'./globals.css';exportdefaultfunctionRootLayout({ children }: {children: React.ReactNode}){return(<htmllang="en"><body>{children}</body></html>);}Step 3: Use the component in your pages
// app/page.tsximportToastButtonfrom'./components/ToastButton';exportdefaultfunctionHome(){return(<main><h1>My Next.js App</h1><ToastButton/></main>);}// pages/_app.tsximporttype{AppProps}from'next/app';import'notifyx/style.css';import'../styles/globals.css';exportdefaultfunctionApp({ Component, pageProps }: AppProps){return<Component{...pageProps}/>;}// pages/index.tsximportNotifyXfrom'notifyx';exportdefaultfunctionHome(){return(<buttononClick={()=>NotifyX.success('Next.js Pages Router!')}> Show Toast </button>);}// app/api/save/route.tsimport{NextResponse}from'next/server';exportasyncfunctionPOST(request: Request){try{constdata=awaitrequest.json();// Process data...returnNextResponse.json({success: true,message: 'Data saved successfully!'});}catch(error){returnNextResponse.json({success: false,message: 'Failed to save data'},{status: 500});}}// Client component using the API'use client';importNotifyXfrom'notifyx';exportdefaultfunctionSaveButton(){consthandleSave=async()=>{try{constresponse=awaitfetch('/api/save',{method: 'POST',body: JSON.stringify({data: 'example'})});constresult=awaitresponse.json();if(result.success){NotifyX.success(result.message);}else{NotifyX.error(result.message);}}catch(error){NotifyX.error('Network error occurred');}};return<buttononClick={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>importNotifyXfrom'notifyx';import'notifyx/style.css';constshowSuccess= () =>{NotifyX.success('Operation successful!');};constshowError= () =>{NotifyX.error('Something went wrong!');};constshowWarning= () =>{NotifyX.warning('Please be careful!');};constshowInfo= () =>{NotifyX.info('Here is some information.');};</script>// composables/useNotify.tsimportNotifyXfrom'notifyx';exportfunctionuseNotify(){constsuccess=(message: string)=>NotifyX.success(message);consterror=(message: string)=>NotifyX.error(message);constwarning=(message: string)=>NotifyX.warning(message);constinfo=(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';constnotify=useNotify();</script>// app.component.tsimport{Component}from'@angular/core';importNotifyXfrom'notifyx'; @Component({selector: 'app-root',template: ` <button (click)="showNotification()">Show Toast</button> `,styles: []})exportclassAppComponent{showNotification(){NotifyX.success('Hello from Angular!');}}{"styles": [ "src/styles.css", "node_modules/notifyx/style.css" ] }// services/notification.service.tsimport{Injectable}from'@angular/core';importNotifyXfrom'notifyx'; @Injectable({providedIn: 'root'})exportclassNotificationService{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.tsimport{Component}from'@angular/core';import{NotificationService}from'./services/notification.service'; @Component({selector: 'app-root',template: `<button (click)="save()">Save</button>`})exportclassAppComponent{constructor(privatenotification: NotificationService){}save(){this.notification.success('Saved successfully!');}}Step 1: Install NotifyX
npm install notifyxStep 2: Import in your main JavaScript file
// resources/js/app.jsimportNotifyXfrom'notifyx';import'notifyx/style.css';// Make NotifyX globally availablewindow.NotifyX=NotifyX;// Example helper functionwindow.showToast=(message,type='info')=>{NotifyX[type](message);};Step 3: Use in Blade templates
{{-- resources/views/layouts/app.blade.php --}} <!DOCTYPE html> <html> <head> <metacharset="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') <formaction="/posts"method="POST"onsubmit="handleSubmit(event)"> @csrf <inputtype="text"name="title"required> <buttontype="submit">Create Post</button> </form> <script>functionhandleSubmit(event){// Client-side notificationshowToast('Creating post...', 'info');}</script> @endsection{{-- resources/views/layouts/app.blade.php --}} <!DOCTYPE html> <html> <head> <metacharset="utf-8"> <title>My Laravel App</title> <linkrel="stylesheet"href="https://unpkg.com/notifyx@latest/dist/notifyx.min.css"> </head> <body> @yield('content') <scriptsrc="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<?phpnamespaceApp\Http\Controllers; useIlluminate\Http\Request; class PostController extends Controller{publicfunctionstore(Request$request){$request->validate([ 'title' => 'required|max:255', 'content' => 'required', ]); // Save post...returnredirect('/posts') ->with('success', 'Post created successfully!')} publicfunctiondestroy($id){try{// Delete post...returnredirect('/posts') ->with('success', 'Post deleted successfully!')} catch (\Exception$e){returnredirect('/posts') ->with('error', 'Failed to delete post.')} } }Perfect for static sites, WordPress, or any HTML page:
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport" content="width=device-width, initial-scale=1.0"><title>NotifyX Demo</title><!-- NotifyX CSS --><linkrel="stylesheet" href="https://unpkg.com/notifyx@latest/dist/notifyx.min.css"></head><body><h1>NotifyX Vanilla JS Demo</h1><buttononclick="NotifyX.success('Success!')">Success</button><buttononclick="NotifyX.error('Error!')">Error</button><buttononclick="NotifyX.warning('Warning!')">Warning</button><buttononclick="NotifyX.info('Info!')">Info</button><buttononclick="NotifyX.clear()">Clear All</button><!-- NotifyX JavaScript --><scriptsrc="https://unpkg.com/notifyx@latest/dist/notifyx.min.js"></script><script>// Show welcome message on page loadwindow.addEventListener('DOMContentLoaded',()=>{NotifyX.info('Welcome! Click any button to see notifications.',{duration: 5000,position: 'top-right'});});// Custom function with configurationfunctionshowCustomToast(){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:
importNotifyX,{ToastOptions,ToastType,Position,POSITIONS,DEFAULT_OPTIONS}from'notifyx';// Type-safe notificationconstoptions: ToastOptions={message: 'Type-safe notification!',type: 'success',position: 'top-right',duration: 3000,dismissible: true,onClose: ()=>console.log('Closed')};NotifyX.show(options);// Using type unionsconstnotificationType: ToastType='error';constnotificationPosition: Position=POSITIONS.BOTTOM_LEFT;NotifyX.show({message: 'Using TypeScript types',type: notificationType,position: notificationPosition});typeToastType='success'|'error'|'warning'|'info';typePosition='top-right'|'top-left'|'bottom-right'|'bottom-left';interfaceToastOptions{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:16px20px; font-family:'Inter', sans-serif; box-shadow:010px40pxrgba(0,0,0,0.2)} /* Success toast gradient */ .notifyx-success{background:linear-gradient(135deg,#667eea0%,#764ba2100%); color: white; border: none} /* Error toast gradient */ .notifyx-error{background:linear-gradient(135deg,#f093fb0%,#f5576c100%); 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,#10b9810%,#059669100%)} } /* 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 classesconsttoast=document.querySelector('.notifyx:last-child');toast.classList.add('backdrop-blur-md','shadow-2xl','ring-2','ring-blue-500');Or create a wrapper function:
functionshowStyledToast(message,type='info'){NotifyX[type](message);setTimeout(()=>{consttoast=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 actionsNotifyX.success('Copied!',{duration: 1000});// Important infoNotifyX.warning('Read carefully',{duration: 5000});
Batch Operations: Clear between batches
functionprocessBatch(){NotifyX.clear();// Clear previousNotifyX.info('Processing batch...');}
Problem: Notifications appear but have no styles.
Solution: Make sure you've imported the CSS:
import'notifyx/style.css';For CDN:
<linkrel="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:
<scriptsrc="https://unpkg.com/notifyx@latest/dist/notifyx.min.js"></script><script>// Now you can use NotifyXNotifyX.success('Works!');</script>Problem: Error during server-side rendering.
Solution: Use client components:
'use client';// Add this at the topimportNotifyXfrom'notifyx';Problem: Multiple toasts appear on re-renders.
Solution: Use useCallback or useMemo:
constshowToast=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!