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.

SEO Impact 2025

"Searcher Engagement" is now a top-tier ranking factor (weighted at 12%). Intrusive interstitials—like immediate permission prompts—cause "pogo-sticking" (users immediately bouncing back to search results). Google's algorithm detects this pattern and demotes sites that frustrate users within seconds of loading.

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.