Calling Notification.requestPermission()triggers the browser's native permission dialog, where the user can grant, deny, or dismiss push notifications. Ask only in direct response to a user gesture tied to a clear notification benefit, never on page load. Show a custom pre-prompt first, because a denial in the native dialog is sticky and effectively permanent.
The right moment ties the request to a feature the user just expressed interest in. The wrong moment is anything that interrupts a stranger.
| Scenario | Ask? | Why |
|---|---|---|
| User clicks "Notify me when back in stock" | Yes | Direct gesture, obvious benefit |
| User completes a purchase and opts into shipping alerts | Yes | Specific, time-bound value |
| User toggles "Enable breaking news" in settings | Yes | Explicit intent to subscribe |
| Visitor lands on the homepage | No | No relationship, no gesture; the automated audit fails |
| User scrolls 50 percent of an article | No | Not a gesture tied to notifications |
| User dismisses a previous pre-prompt | No | Wait at least 30 days before re-asking |
DOMContentLoaded, or inside analytics scripts.Notification.requestPermission().localStorage and suppress the pre-prompt for at least 30 days.granted, denied, default (dismissed).// Bind to a real user gesture, never to page load
document.querySelector('#notify-btn').addEventListener('click', async () => {
// 1. Custom pre-prompt — a denial here is recoverable
const userAccepted = await showPrePrompt({
title: 'Get notified when this ships',
body: 'We will send one notification per shipping update. Nothing else.',
});
if (!userAccepted) {
localStorage.setItem('notify-dismissed', Date.now());
return;
}
// 2. Only now trigger the native dialog
const result = await Notification.requestPermission();
if (result === 'granted') {
await subscribeToPush();
}
});function canShowPrePrompt() {
if (Notification.permission !== 'default') return false;
const dismissed = Number(localStorage.getItem('notify-dismissed') || 0);
const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
return Date.now() - dismissed > THIRTY_DAYS;
}Notification.requestPermission() in window.onload or a top-level script — instant audit fail and triggers Chrome's quieter UI.default (dismissed) as a denial; the user can still be re-prompted.chrome://settings/content/notifications, reset the site, and reload to verify no prompt appears unprompted.See also our guides on geolocation permission timing and passive event listeners for related UX patterns.
The audit "Avoids requesting the notification permission on page load" fails when Notification.requestPermission() runs without a user gesture, because users overwhelmingly deny these prompts and a denial is permanent.
Introduced in Chrome 80 (2020), it replaces the prompt with a small bell icon for sites that show a high block rate or that auto-prompt. Most users never click the bell, effectively suppressing your prompt entirely.
No. Once the user clicks "Block" in the native dialog, the browser will not show the prompt again. The user must manually re-enable notifications in site settings, which almost never happens.
Notification.permission returns denied when the user explicitly blocked, and default when they closed the prompt without choosing. default is recoverable, denied is not.
Yes. The pre-prompt explains the value before the irreversible native dialog. Sites using a pre-prompt typically see grant rates 2 to 4 times higher than going straight to the native prompt.
If the user dismisses the pre-prompt (not the native one), wait at least 30 days. The pre-prompt is yours to control; the native one is not.
Yes, since Safari 16 on macOS and Safari 16.4 on iOS for installed web apps. It still requires a transient user gesture and keeps denials sticky.
Yes, indirectly. Aggressive permission prompts on page load drive sharp bounce rates and short sessions, which feed user-engagement signals that show up in Page Experience and in the third-party usage data AI search engines like ChatGPT, Perplexity, and Google AI Overviews use to weight a source. A page that visitors abandon within seconds is far less likely to be cited as a credible answer, regardless of how good the underlying content is.
Ask for notification permission only in direct response to a user gesture tied to a specific notification benefit, and always show a pre-prompt before the native dialog so a refusal stays recoverable. Auto-prompting on page load fails automated audits, triggers Chrome's quieter UI, and burns the user's only meaningful answer — replace it with a feature-entry button and a 30-day dismissal cooldown. Run a Greadme deep scan to find pages that request notification permission too aggressively and surface the ones costing you engagement.