Fix Notification Request on Page Load

Stop requesting notification permission on page load. Users dismiss these prompts reflexively - wait for a user action that shows they want notifications.
Harlan WiltonHarlan Wilton3 min read Published

Nothing says "desperate" like a notification prompt the instant someone opens your site. Users don't know you yet, don't trust you, and will click Block without thinking. Now your notification features are dead for that user forever.

What's Happening

Your code calls Notification.requestPermission() during page load - before the user has expressed any interest in receiving notifications. The browser shows a permission prompt, the user blocks it reflexively, and that decision persists until they manually dig through browser settings to reverse it.

Chrome has started hiding these prompts entirely for sites that abuse them. Your notification request might not even be shown anymore if your site has a poor grant rate.

Diagnose

DevTools Console

Check the Lighthouse audit details for the exact source location. To find it manually:

  1. Open DevTools > Sources
  2. Search all files (Cmd/Ctrl+Shift+F) for Notification.requestPermission or pushManager.subscribe
  3. Check if calls are inside event handlers (good) or running automatically (bad)

Patterns That Trigger This

// Bad: Runs immediately
Notification.requestPermission()

// Bad: Runs on page load
document.addEventListener('DOMContentLoaded', () => {
  Notification.requestPermission()
})

// Bad: In module top-level
if (Notification.permission === 'default') {
  Notification.requestPermission()
}

// Bad: Auto-runs after slight delay
setTimeout(() => Notification.requestPermission(), 3000)

Fix

1. Request After User Action

Move the permission request inside a user-triggered event:

Before (bad):

// Runs on page load
Notification.requestPermission().then((permission) => {
  if (permission === 'granted') {
    subscribeToNotifications()
  }
})

After (good):

document.querySelector('#enable-notifications').addEventListener('click', () => {
  Notification.requestPermission().then((permission) => {
    if (permission === 'granted') {
      subscribeToNotifications()
      updateButtonState('enabled')
    }
    else {
      updateButtonState('denied')
    }
  })
})

The user clicks a button that says "Enable notifications," the browser prompt appears, and context is clear.

2. Explain Value Before Asking

Show users what they'll get before triggering the browser prompt:

const notifyBtn = document.querySelector('#notify-toggle')

notifyBtn.addEventListener('click', async () => {
  const permission = Notification.permission

  if (permission === 'granted') {
    // Already enabled, maybe show settings
    showNotificationSettings()
  }
  else if (permission === 'denied') {
    // Blocked - explain how to unblock
    showUnblockInstructions()
  }
  else {
    // Show our own explainer first
    showNotificationExplainer()
  }
})

function showNotificationExplainer() {
  const modal = document.querySelector('#notification-explainer')
  modal.innerHTML = `
    <h3>Get notified when:</h3>
    <ul>
      <li>Your order ships</li>
      <li>Someone replies to your comment</li>
      <li>There's a flash sale</li>
    </ul>
    <button id="confirm-notify">Enable Notifications</button>
    <button id="skip-notify">Not Now</button>
  `
  modal.showModal()

  modal.querySelector('#confirm-notify').addEventListener('click', () => {
    modal.close()
    Notification.requestPermission().then(handlePermissionResult)
  }, { once: true })
}

3. Wait for Engagement

Request after the user has shown investment in your site:

// Only show notification option after user has engaged
let userEngaged = false

function checkEngagement() {
  // User has been on site 2+ minutes, or completed an action
  const timeOnSite = Date.now() - pageLoadTime
  const hasInteracted = purchaseComplete || commentPosted || signedUp

  if (timeOnSite > 120000 || hasInteracted) {
    userEngaged = true
    showNotificationPrompt()
  }
}

function showNotificationPrompt() {
  if (Notification.permission !== 'default')
    return

  // Show soft prompt, not browser prompt
  const banner = document.querySelector('#notification-banner')
  banner.classList.remove('hidden')

  banner.querySelector('#enable-btn').addEventListener('click', () => {
    Notification.requestPermission()
    banner.classList.add('hidden')
  }, { once: true })
}

Verify the Fix

  1. Clear site data (DevTools > Application > Clear site data)
  2. Reload the page
  3. No permission prompt should appear automatically
  4. Navigate around, scroll, read content - still no prompt
  5. Click the notification enable button
  6. Now the browser prompt appears

Run Lighthouse - the "Requests the notification permission on page load" audit should pass.

Common Mistakes

  • Using a short delay - setTimeout(() => Notification.requestPermission(), 5000) still runs automatically. Five seconds isn't user engagement; it's just slower annoyance.
  • Checking permission then auto-requesting - "If not denied, request" is still automatic. The user must take action.
  • Third-party chat widgets - Some live chat and support widgets request notification permission automatically. Check their configuration for a "request on user interaction" option.
  • Service worker auto-subscribe - Some push notification libraries subscribe automatically. Configure them to wait for explicit opt-in.

Test Your Entire Site

Notification requests might be triggered by scripts that only load on specific pages, like checkout or account settings. Unlighthouse scans your entire site and identifies every page requesting notification permission on load.