Реализация Notifications API на сайте
Push-уведомления в браузере без мобильного приложения. Пользователь разрешил — получает уведомления даже когда вкладка закрыта (при наличии Service Worker). Не разрешил — работаем с fallback в UI.
Notifications API и Push API — разные вещи. Notifications API показывает уведомление через браузер. Push API доставляет событие с сервера. Для уведомлений при закрытой вкладке нужны оба.
Запрос разрешения
Главное правило — запрашивать разрешение только после явного действия пользователя, иначе браузер заблокирует запрос автоматически:
async function requestNotificationPermission(): Promise<NotificationPermission> {
if (!('Notification' in window)) {
throw new Error('Notifications API не поддерживается')
}
if (Notification.permission === 'granted') return 'granted'
if (Notification.permission === 'denied') return 'denied'
// Вызываем только из обработчика события (click, submit и т.д.)
return Notification.requestPermission()
}
Показ уведомления
interface NotificationOptions {
title: string
body?: string
icon?: string
badge?: string
tag?: string // Группировка — новое уведомление заменит старое с тем же tag
requireInteraction?: boolean // Не закрывать автоматически
data?: unknown
actions?: NotificationAction[] // Кнопки в уведомлении (только в Service Worker)
}
function showNotification(options: NotificationOptions): Notification | null {
if (Notification.permission !== 'granted') return null
const { title, ...rest } = options
const notification = new Notification(title, rest)
notification.onclick = (event) => {
event.preventDefault()
window.focus()
notification.close()
// Переход к нужному разделу по notification.data
}
return notification
}
Уведомления через Service Worker
Для уведомлений при закрытой вкладке — только через SW:
// service-worker.ts
self.addEventListener('push', (event: PushEvent) => {
const data = event.data?.json() ?? {}
event.waitUntil(
self.registration.showNotification(data.title ?? 'Новое уведомление', {
body: data.body,
icon: '/icons/notification-icon-192.png',
badge: '/icons/badge-72.png',
tag: data.tag ?? 'default',
data: { url: data.url },
actions: [
{ action: 'open', title: 'Открыть' },
{ action: 'dismiss', title: 'Закрыть' },
],
})
)
})
self.addEventListener('notificationclick', (event: NotificationEvent) => {
event.notification.close()
if (event.action === 'dismiss') return
const url = event.notification.data?.url ?? '/'
event.waitUntil(
clients.matchAll({ type: 'window' }).then((windowClients) => {
const existingClient = windowClients.find((c) => c.url === url)
if (existingClient) return existingClient.focus()
return clients.openWindow(url)
})
)
})
React-хук
function useNotifications() {
const [permission, setPermission] = useState<NotificationPermission>(
typeof Notification !== 'undefined' ? Notification.permission : 'denied'
)
const [supported] = useState(() => 'Notification' in window)
const request = useCallback(async () => {
if (!supported) return
const result = await requestNotificationPermission()
setPermission(result)
}, [supported])
const notify = useCallback(
(options: NotificationOptions) => {
if (permission !== 'granted') return null
return showNotification(options)
},
[permission]
)
return { supported, permission, request, notify }
}
Обработка состояний разрешения в UI
function NotificationSettings() {
const { supported, permission, request, notify } = useNotifications()
if (!supported) {
return <p>Уведомления не поддерживаются вашим браузером</p>
}
return (
<div>
{permission === 'default' && (
<button onClick={request}>Включить уведомления</button>
)}
{permission === 'granted' && (
<button onClick={() => notify({ title: 'Тест', body: 'Уведомления работают' })}>
Проверить
</button>
)}
{permission === 'denied' && (
<p>Уведомления заблокированы. Разрешите в настройках браузера.</p>
)}
</div>
)
}
Что входит в работу
Реализация утилит запроса разрешений и показа уведомлений, React-хук, обработка всех состояний (default, granted, denied), опционально — интеграция с Service Worker для Push API и VAPID-ключами на бэкенде.
Срок: 0.5–1 день (без Push API). С Push API и настройкой бэкенда — 1–2 дня.







