universal-push-notifications
v1.1.0
Published
A secure, universal push notification package for React.js and Vue.js with server-side environment variable protection
Maintainers
Readme
Universal Push Notifications
A secure, universal push notification package for React.js and Vue.js applications with server-side environment variable protection.
Features
- 🔒 Secure: Environment variables kept server-side
- ⚛️ React Support: Custom hook with TypeScript support
- 🟢 Vue Support: Composable API for Vue 2 & 3
- 🚀 Easy Setup: Simple configuration and setup
- 📱 PWA Ready: Service worker included
- 🔧 Customizable: Flexible configuration options
- 📦 Lightweight: Minimal dependencies
Package Structure
This package is organized into client and server parts for better separation of concerns:
Client-side imports (React/Vue components):
// ESM import { usePushNotifications } from 'universal-push-notifications'; // or CommonJS const { usePushNotifications } = require('universal-push-notifications');Server-side imports (nodejs/express applications):
// ESM import { NotificationServer } from 'universal-push-notifications/server'; // or CommonJS const { NotificationServer } = require('universal-push-notifications/server');
Installation
npm install universal-push-notifications
# or
yarn add universal-push-notificationsQuick Start
1. Generate VAPID Keys
const { NotificationServer } = require('universal-push-notifications/server');
// Generate keys (run once, store securely)
const vapidKeys = NotificationServer.generateVapidKeys();
console.log(vapidKeys);
// Save these to your environment variables2. Server Setup (Express.js)
const express = require('express');
// Import server module specifically
const { NotificationServer } = require('universal-push-notifications/server');
const app = express();
app.use(express.json());
const notificationServer = new NotificationServer({
vapidKeys: {
publicKey: process.env.VAPID_PUBLIC_KEY,
privateKey: process.env.VAPID_PRIVATE_KEY
},
vapidDetails: {
subject: 'mailto:[email protected]'
}
});
const middleware = notificationServer.createMiddleware();
app.get('/api/notifications/vapid-public-key', middleware.getVapidPublicKey);
app.post('/api/notifications/subscribe', middleware.subscribe);
app.post('/api/notifications/unsubscribe', middleware.unsubscribe);
app.post('/api/notifications/send', middleware.sendNotification);
app.listen(3000);3. Copy Service Worker
Copy the service worker to your public folder:
cp node_modules/universal-push-notifications/public/sw.js public/4. React Usage
import React from 'react';
import { usePushNotifications } from 'universal-push-notifications';
function NotificationComponent() {
const {
isSupported,
subscription,
isSubscribed,
permission,
loading,
error,
subscribe
} = usePushNotifications({
apiEndpoint: '/api/notifications',
debug: true
});
if (!isSupported) {
return <div>Push notifications not supported</div>;
}
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
return (
<div>
<h3>Push Notifications</h3>
<p>Current permission: {permission}</p>
{!isSubscribed ? (
<button onClick={subscribe} disabled={loading}>
Enable Push Notifications
</button>
) : (
<div>
<p>Notifications enabled!</p>
<pre>{JSON.stringify(subscription, null, 2)}</pre>
</div>
)}
</div>
);
}5. Vue Usage
// Component.js
import { createPushNotifications } from 'universal-push-notifications';
export default {
setup() {
const {
isSupported,
subscription,
isSubscribed,
permission,
loading,
error,
subscribe
} = createPushNotifications({
apiEndpoint: '/api/notifications',
debug: true
});
return {
isSupported,
isSubscribed,
permission,
loading,
error,
subscribe,
subscription
};
}
};<!-- Component.vue -->
<template>
<div>
<h3>Push Notifications</h3>
<div v-if="!isSupported">
Push notifications not supported
</div>
<div v-else-if="loading">
Loading...
</div>
<div v-else-if="error">
Error: {{ error }}
</div>
<div v-else>
<p>Current permission: {{ permission }}</p>
<button
v-if="!isSubscribed"
@click="subscribe"
:disabled="loading"
>
Enable Push Notifications
</button>
<div v-else>
<p>Notifications enabled!</p>
<pre>{{ JSON.stringify(subscription, null, 2) }}</pre>
</div>
</div>
</div>
</template>6. Send Notifications from Server
<template>
<div v-if="!isSupported">
Push notifications not supported
</div>
<div v-else>
<h3>Push Notifications</h3>
<p>Permission: {{ permission }}</p>
<p>Subscribed: {{ isSubscribed ? 'Yes' : 'No' }}</p>
<p v-if="error" style="color: red">Error: {{ error }}</p>
<button
@click="subscribe"
:disabled="loading || isSubscribed"
>
{{ loading ? 'Loading...' : 'Subscribe' }}
</button>
<button
@click="unsubscribe"
:disabled="loading || !isSubscribed"
>
{{ loading ? 'Loading...' : 'Unsubscribe' }}
</button>
<button
@click="() => showTestNotification('Test', { body: 'This is a test!' })"
:disabled="!isSubscribed"
>
Test Notification
</button>
</div>
</template>
<script>
import { createPushNotificationsVue2 } from 'universal-push-notifications';
export default createPushNotificationsVue2({
apiEndpoint: '/api/notifications',
debug: true
});
</script>Configuration Options
Client Configuration
const config = {
apiEndpoint: '/api/notifications', // API endpoint base path
swPath: '/sw.js', // Service worker path
debug: false // Enable debug logging
};Server Configuration
const notificationServer = new NotificationServer({
vapidKeys: {
publicKey: 'YOUR_PUBLIC_KEY',
privateKey: 'YOUR_PRIVATE_KEY'
},
vapidDetails: {
subject: 'mailto:[email protected]' // or 'https://yoursite.com'
}
});API Reference
React Hook: usePushNotifications(config)
Returns an object with:
isSupported: boolean - Browser supportsubscription: PushSubscription | null - Current subscriptionisSubscribed: boolean - Subscription statuspermission: NotificationPermission - Current permissionloading: boolean - Loading stateerror: string | null - Error messagesubscribe(): Function to subscribeunsubscribe(): Function to unsubscribeshowTestNotification(title, options): Show test notificationclearError(): Clear error state
Vue Composable: createPushNotifications(config)
Returns an object with:
state: Reactive state objectisSubscribed: Computed subscription statussubscribe(): Function to subscribeunsubscribe(): Function to unsubscribeshowTestNotification(title, options): Show test notificationclearError(): Clear error stateinitialize(): Manual initialization
Server Class: NotificationServer
Methods:
static generateVapidKeys(): Generate VAPID key paircreateMiddleware(): Create Express middlewaregetAllSubscriptions(): Get all subscriptionsgetSubscriptionCount(): Get subscriber count
Environment Variables
# Required VAPID keys (keep these secret!)
VAPID_PUBLIC_KEY=your_public_key_here
VAPID_PRIVATE_KEY=your_private_key_here
# Optional
NODE_ENV=productionSecurity Features
✅ Environment Variables Protected: VAPID private keys never exposed to client ✅ Secure API Endpoints: Proper validation and error handling ✅ HTTPS Required: Push notifications require HTTPS in production ✅ Origin Validation: Service worker validates notification origin ✅ Subscription Management: Automatic cleanup of invalid subscriptions
Browser Support
- Chrome/Edge: ✅ Full support
- Firefox: ✅ Full support
- Safari: ✅ iOS 16.4+ / macOS 13+
- Opera: ✅ Full support
Troubleshooting
Common Issues
- Notifications not showing: Check browser permissions and HTTPS
- Service worker not registering: Verify file path and HTTPS
- VAPID errors: Ensure keys are correctly set in environment
- Cross-origin issues: Check CORS settings on your server
Debug Mode
Enable debug logging:
const notifications = usePushNotifications({ debug: true });Examples
Check the examples/ directory for complete working examples:
examples/react-example/- Complete React appexamples/vue-example/- Complete Vue appexamples/server-example/- Express.js server setup
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT License
