
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
A modern, lightweight push notification library for Next.js applications with full TypeScript support.
# Using npm
npm install next-push
# Using yarn
yarn add next-push
# Using pnpm
pnpm add next-push
npm install next-push
NEXT_PUBLIC_VAPID_PUBLIC_KEY=your-public-key
VAPID_PRIVATE_KEY=your-private-key
// /public/sw.js
self.addEventListener('install', (event) => {
event.waitUntil(self.skipWaiting());
});
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
});
self.addEventListener('push', (event) => {
if (!event.data) {
console.log('No data received');
return;
}
try {
const data = event.data.json();
const options = {
body: data.message,
icon: data.icon || '/icon.png',
badge: '/badge.png',
data: {
url: data.url,
...data
},
requireInteraction: false,
silent: false,
tag: data.tag || 'next-push-notification'
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
} catch (error) {
console.error('Error processing push notification:', error);
}
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
if (event.notification.data?.url) {
event.waitUntil(
self.clients.openWindow(event.notification.data.url)
);
}
});
self.addEventListener('notificationclose', (event) => {
});
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
import { NextPushProvider } from 'next-push/client';
function App() {
return (
<NextPushProvider>
<YourApp />
</NextPushProvider>
);
}
import { useNextPushContext } from 'next-push/client';
function MyComponent() {
const { subscribe, subscribed } = useNextPushContext();
return (
<button onClick={subscribe}>
{subscribed ? 'Unsubscribe' : 'Subscribe'}
</button>
);
}
That's it! 🎉
import { NextPushProvider, useNextPushContext } from 'next-push/client';
// Wrap your app with NextPushProvider
function App() {
return (
<NextPushProvider>
<MyComponent />
</NextPushProvider>
);
}
const MyComponent = () => {
const {
isSupported,
subscribed,
loading,
permission,
error,
subscription,
subscribe,
unsubscribe
} = useNextPushContext(); // VAPID key otomatik olarak environment variable'dan alınır
const handleSubscribe = async () => {
try {
await subscribe();
console.log('Successfully subscribed to push notifications');
console.log('Subscription:', subscription);
} catch (error) {
console.error('Subscription failed:', error);
}
};
return (
<div>
{error && (
<div style={{ color: 'red' }}>
Error: {error.message}
</div>
)}
{isSupported ? (
<div>
<p>Permission: {permission}</p>
<p>Subscribed: {subscribed ? 'Yes' : 'No'}</p>
{subscription && (
<p>Subscription: {subscription.endpoint.slice(0, 50)}...</p>
)}
{!subscribed && (
<button
onClick={handleSubscribe}
disabled={loading}
>
{loading ? 'Subscribing...' : 'Subscribe to Notifications'}
</button>
)}
{subscribed && (
<button onClick={unsubscribe}>
Unsubscribe
</button>
)}
</div>
) : (
<p>Push notifications are not supported in this browser</p>
)}
</div>
);
};
import { createServerPush } from 'next-push/server';
// Generate VAPID keys
const vapidKeys = {
publicKey: 'your-public-key',
privateKey: 'your-private-key'
};
const pushServer = createServerPush('admin@example.com', vapidKeys);
// Send notification to a single subscription
const sendNotification = async (subscription: PushSubscription) => {
const result = await pushServer.sendNotification(subscription, {
title: 'Hello!',
message: 'This is a test notification',
url: 'https://example.com',
icon: '/icon.png'
});
if (result.success) {
console.log('Notification sent successfully');
} else {
console.error('Failed to send notification:', result.error);
}
};
// Send notification to multiple subscriptions
const sendToAll = async (subscriptions: PushSubscription[]) => {
const result = await pushServer.sendNotificationToAll(subscriptions, {
title: 'Broadcast Message',
message: 'This message was sent to all subscribers',
url: 'https://example.com'
});
console.log(`Sent to ${result.successful} users, ${result.failed} failed`);
};
next-push/client)NextPushProviderA React context provider that automatically sets up the service worker and provides push notification functionality.
useNextPushContext()A React hook that provides push notification functionality. Must be used within a NextPushProvider.
config.vapidPublicKey (string): Your VAPID public keyconfig.defaultIcon? (string): Default icon URL for notificationsconfig.onNotificationClick? (function): Callback when notification is clickedisSupported (boolean): Whether push notifications are supportedsubscribed (boolean): Current subscription status (shorter name)loading (boolean): Loading state for async operations (shorter name)permission (NotificationPermission): Current notification permissionerror (PushError | null): Current error state with detailed error typesprogress (PushProgress): Current operation progresssubscription (PushSubscription | null): Current push subscription objectsubscribe() (function): Subscribe to push notificationsunsubscribe() (function): Unsubscribe from push notificationstoggle() (function): Toggle subscription statereset() (function): Reset error state and progresscheck() (function): Check current subscription status (shorter name)getSubscription() (function): Get current subscriptioninterface PushConfig {
vapidPublicKey?: string; // Optional - automatically loaded from environment variables
defaultIcon?: string;
onNotificationClick?: (url?: string) => void;
logger?: (message: string, type: 'info' | 'error') => void;
retryAttempts?: number; // Number of retry attempts (default: 3)
retryDelay?: number; // Delay between retries in ms (default: 1000)
autoSubscribe?: boolean; // Automatically subscribe when permission is granted
}
interface NotificationData {
title: string;
message: string;
url?: string;
icon?: string;
}
next-push/server)createServerPush(mail: string, vapidKeys: VapidKeys)Creates a server-side push notification handler.
mail (string): Email address for VAPID configurationvapidKeys (object): VAPID public and private keyssendNotification(subscription, data) (function): Send notification to single subscriptionsendNotificationToAll(subscriptions, data) (function): Send notification to multiple subscriptions# Using web-push library
npx web-push generate-vapid-keys
Next-Push requires a service worker file to handle push notifications. You need to create a service worker file in your public directory.
Create Service Worker File:
Create public/sw.js with the following content:
// Next-Push Service Worker
self.addEventListener('install', (event) => {
event.waitUntil(self.skipWaiting());
});
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
});
self.addEventListener('push', (event) => {
if (!event.data) {
console.log('No data received');
return;
}
try {
const data = event.data.json();
const options = {
body: data.message,
icon: data.icon || '/icon.png',
badge: '/badge.png',
data: {
url: data.url,
...data
},
requireInteraction: false,
silent: false,
tag: data.tag || 'next-push-notification'
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
} catch (error) {
console.error('Error processing push notification:', error);
}
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
if (event.notification.data?.url) {
event.waitUntil(
self.clients.openWindow(event.notification.data.url)
);
}
});
self.addEventListener('notificationclose', (event) => {
});
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
# Client-side (public)
NEXT_PUBLIC_VAPID_PUBLIC_KEY=your-public-key
# Server-side (private)
VAPID_PRIVATE_KEY=your-private-key
Next-Push automatically looks for VAPID public key in this order:
useNextPush({ vapidPublicKey: '...' })window.NEXT_PUSH_VAPID_PUBLIC_KEYNEXT_PUBLIC_VAPID_PUBLIC_KEY// Automatic - loads from environment variable
const { subscribe } = useNextPush();
// Manual - loads from config
const { subscribe } = useNextPush({
vapidPublicKey: 'your-key-here'
});
If you're upgrading from an older version:
// Old way
const { isSubscribed, isLoading, checkSubscription } = useNextPush({
vapidPublicKey: 'required-key'
});
// New way
const { subscribed, loading, check } = useNextPush(); // VAPID key automatic
// pages/index.tsx
import { useNextPush } from 'next-push/client';
export default function Home() {
const { isSupported, subscribed, subscription, subscribe, unsubscribe } = useNextPush(); // VAPID key automatic
return (
<div>
<h1>Push Notification Demo</h1>
{isSupported && (
<div>
<button onClick={subscribed ? unsubscribe : subscribe}>
{subscribed ? 'Unsubscribe' : 'Subscribe'}
</button>
{subscription && (
<p>Subscription active: {subscription.endpoint.slice(0, 50)}...</p>
)}
</div>
)}
</div>
);
}
// _app.tsx
import { NextPushProvider } from 'next-push/client';
export default function App({ Component, pageProps }) {
return (
<NextPushProvider>
<Component {...pageProps} />
</NextPushProvider>
);
}
// Any component
import { useNextPushContext } from 'next-push/client';
const MyComponent = () => {
const { subscribe, subscribed, subscription } = useNextPushContext();
return (
<div>
<button onClick={subscribe} disabled={subscribed}>
{subscribed ? 'Subscribed' : 'Subscribe'}
</button>
{subscription && (
<p>Subscription active</p>
)}
</div>
);
};
// pages/api/send-notification.ts
import { createServerPush } from 'next-push/server';
import type { NextApiRequest, NextApiResponse } from 'next';
const pushServer = createServerPush(
'admin@example.com',
{
publicKey: process.env.VAPID_PUBLIC_KEY!,
privateKey: process.env.VAPID_PRIVATE_KEY!
}
);
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' });
}
const { subscription, notification } = req.body;
try {
const result = await pushServer.sendNotification(subscription, notification);
if (result.success) {
res.status(200).json({ message: 'Notification sent successfully' });
} else {
res.status(500).json({ error: result.error });
}
} catch (error) {
res.status(500).json({ error: 'Failed to send notification' });
}
}
// types/push.ts
import type { NotificationData } from 'next-push/client';
export interface CustomNotificationData extends NotificationData {
priority?: 'high' | 'normal';
tag?: string;
}
// components/PushNotification.tsx
import { useNextPush } from 'next-push/client';
import type { PushConfig } from 'next-push/client';
interface CustomPushConfig extends PushConfig {
onError?: (error: Error) => void;
}
export const PushNotification = ({ config }: { config: CustomPushConfig }) => {
const { isSupported, subscribe, subscribed } = useNextPush(config);
// Component implementation
};
import { useNextPush } from 'next-push/client';
const NotificationComponent = () => {
const { subscribe, error, progress } = useNextPush();
if (error) {
switch (error.type) {
case 'PERMISSION_DENIED':
return <div>Please enable notifications in browser settings</div>;
case 'VAPID_MISSING':
return <div>VAPID key not configured</div>;
case 'NOT_SUPPORTED':
return <div>Push notifications not supported in this browser</div>;
default:
return <div>Error: {error.message}</div>;
}
}
return (
<div>
<p>Progress: {progress}</p>
<button onClick={subscribe}>Subscribe</button>
</div>
);
};
const { subscribe } = useNextPush({
retryAttempts: 5,
retryDelay: 2000
});
// Automatically retries failed operations
await subscribe(); // Will retry up to 5 times with 2 second delays
const { subscribed } = useNextPush({
autoSubscribe: true
});
// Automatically subscribes when permission is granted
// No manual subscribe() call needed!
const { toggle, reset, subscribed } = useNextPush();
// Toggle subscription state
<button onClick={toggle}>
{subscribed ? 'Disable' : 'Enable'} Notifications
</button>
// Reset error state and progress
<button onClick={reset}>Reset</button>
next-push/
├── client/ # Client-side module
│ ├── index.ts # React hooks and types
│ └── ...
└── server/ # Server-side module
├── index.ts # Server utilities
└── ...
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)MIT © 2025
1. VAPID Key Error
Error: VAPID public key is required
Solution: Set NEXT_PUBLIC_VAPID_PUBLIC_KEY environment variable or pass it in config.
2. Permission Denied
Error: Notification permission denied
Solution: User must manually enable notifications in browser settings.
3. Permission Not Granted
Error: Notification permission not granted
Solution: User declined the permission request. They can enable it later in browser settings.
4. Service Worker Registration Failed
Service Worker registration failed
Solution: Make sure you're using HTTPS in production (required for service workers).
5. Push Notifications Not Supported
Push notifications not supported
Solution: Check if browser supports service workers and push API.
6. Subscription Failed
Subscription failed
Solution: Check network connection and VAPID key configuration. The library will automatically retry.
Enable debug logging:
const { subscribe } = useNextPush({
logger: (message, type) => {
console.log(`[Next-Push ${type}]:`, message);
}
});
If you encounter any issues or have questions, please open an issue on GitHub.
FAQs
A modern and powerful push notification library for Next.js
The npm package next-push receives a total of 0 weekly downloads. As such, next-push popularity was classified as not popular.
We found that next-push demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.