JavaScript PushManager hasPermission() Method

Beginner
⏱️ 10 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
Deprecated
Non-standard
Instance method

What You’ll Learn

PushManager.hasPermission() is a deprecated, non-standard instance method that returned push permission as granted, denied, or default. Learn what it did, why MDN replaced it with permissionState(), how to feature-detect it, and modern alternatives—with five examples and try-it labs.

01

Kind

Instance method

02

Returns

Promise

03

Values

granted / denied

04

Status

Deprecated

05

Replace

permissionState

06

Spec

Non-standard

Introduction

Before subscribing to push, your app needs to know whether the user granted permission. Early Push API drafts exposed hasPermission() on PushManager for that check.

MDN now marks it deprecated and non-standard. The standardized replacement is permissionState(), which returns "prompt", "denied", or "granted". This tutorial explains the legacy method so you can read old code and migrate safely.

⚠️
Do not use in new code

For new push features, call registration.pushManager.permissionState({ userVisibleOnly: true }) or read Notification.permission when notifications cover your use case.

Understanding hasPermission()

A legacy instance method on PushManager that queried push permission state asynchronously.

  • Parameters — none.
  • ReturnsPromise<PushPermissionStatus>.
  • granted — push permission allowed.
  • denied — push permission blocked.
  • default — user has not decided yet.
  • Superseded bypermissionState() (Baseline Widely available).
  • Available in Web Workers per MDN (where still supported).

📝 Syntax

JavaScript
pushManager.hasPermission()

Return value

A Promise resolving to "granted", "denied", or "default".

Legacy pattern

JavaScript
// Legacy — do not use in new code
const registration = await navigator.serviceWorker.ready;

if (registration.pushManager.hasPermission) {
  const status = await registration.pushManager.hasPermission();
  console.log(status); // "granted" | "denied" | "default"
}

⚡ Quick Reference

GoalCode / note
Legacy checkawait pushManager.hasPermission()
Modern checkawait pushManager.permissionState({ userVisibleOnly: true })
Feature detectif (pushManager.hasPermission)
Notification shortcutNotification.permission
MDN statusDeprecated · Non-standard
ReplacementpermissionState()

🔍 At a Glance

Four facts to remember about hasPermission().

Async
Promise

Returns status

Values
3 states

granted/denied/default

Status
deprecated

Avoid new use

Use
permissionState

Instead

Examples Gallery

Examples explain legacy hasPermission() and show modern replacements. Use View Output or Try It Yourself.

📚 Legacy Usage

How hasPermission() worked (historical).

Example 1 — Legacy hasPermission() Call

Read push permission after serviceWorker.ready.

JavaScript
navigator.serviceWorker.ready.then((registration) => {
  if (!registration.pushManager.hasPermission) {
    console.log("hasPermission not supported");
    return;
  }

  registration.pushManager
    .hasPermission()
    .then((status) => {
      console.log(status); // "granted" | "denied" | "default"
    });
});
Try It Yourself

How It Works

Always feature-detect—many modern browsers removed this method.

Example 2 — Branch UI on Permission Status

Update button text based on granted / denied / default.

JavaScript
const status = await registration.pushManager.hasPermission();
const btn = document.querySelector(".js-push-button");

if (status === "granted") {
  btn.textContent = "Disable Push Messages";
} else if (status === "denied") {
  btn.textContent = "Push Blocked";
  btn.disabled = true;
} else {
  btn.textContent = "Enable Push Messages";
}
Try It Yourself

How It Works

default means the user has not chosen yet—show an enable prompt.

📈 Migration & Alternatives

Modern replacements for new code.

Example 3 — Feature Detect Before Calling

Safe pattern for maintaining legacy codebases.

JavaScript
async function getPushPermissionLegacy(registration) {
  const pm = registration.pushManager;

  if (typeof pm.hasPermission === "function") {
    return pm.hasPermission();
  }
  if (typeof pm.permissionState === "function") {
    return pm.permissionState({ userVisibleOnly: true });
  }
  return Notification.permission;
}
Try It Yourself

How It Works

Try legacy first only when present; fall back to standard APIs.

Example 4 — Migrate to permissionState() (MDN)

Recommended replacement for new push projects.

JavaScript
const registration = await navigator.serviceWorker.ready;

const state = await registration.pushManager.permissionState({
  userVisibleOnly: true,
});

// "prompt" | "denied" | "granted"
console.log(state);
Try It Yourself

How It Works

MDN maps default (legacy) to prompt (modern).

Example 5 — Simple Check with Notification.permission

When push messages are always user-visible notifications.

JavaScript
// Synchronous — no PushManager call needed
if (Notification.permission === "granted") {
  console.log("Notifications (and push) allowed");
} else if (Notification.permission === "denied") {
  console.log("Blocked");
} else {
  console.log("Not decided yet");
}
Try It Yourself

How It Works

Firefox merged notification and push permissions per MDN’s permissionState notes.

🚀 Common Use Cases (Historical)

  • Reading old push tutorials that call hasPermission().
  • Maintaining legacy PWAs that still feature-detect the method.
  • Understanding why default maps to prompt in modern APIs.
  • Migrating permission UI to permissionState().
  • Teaching the difference between permission checks and getSubscription().

🔧 How It Works

1

SW ready

Get registration.pushManager.

Setup
2

Call hasPermission

Legacy async permission query.

Deprecated
3

Promise resolves

granted, denied, or default.

Status
4

Migrate

Use permissionState() or Notification.permission in new code.

📝 Notes

  • Deprecated & non-standard on MDN—not for new production code.
  • Superseded by permissionState() (Baseline Widely available, Mar 2023).
  • Not Experimental — it was standard-track once but removed from specs.
  • Feature-detect before calling in legacy maintenance.
  • Secure context required for Push API features.
  • Related learning: getSubscription(), supportedContentEncodings, navigator.serviceWorker, JavaScript hub.

Limited Browser Support

PushManager.hasPermission() is Deprecated and Non-standard on MDN. It may be missing in modern browsers. Use permissionState() instead. Logos use the shared browser-image-sprite.png sprite from this project.

Deprecated · Non-standard

PushManager.hasPermission()

Legacy push permission check — avoid in new code.

Legacy Deprecated API
Google Chrome May be removed — use permissionState()
Legacy only
Mozilla Firefox Legacy support may vary
Legacy only
Apple Safari Unlikely — use permissionState()
Not supported
Microsoft Edge Follow Chromium — prefer permissionState()
Legacy only
Opera Follow Chromium
Legacy only
Internet Explorer No Push API
Not supported
hasPermission() Limited

Bottom line: Feature-detect before calling. Prefer permissionState() for new push permission checks.

Conclusion

pushManager.hasPermission() was an early way to read push permission as granted, denied, or default. MDN deprecates it in favor of permissionState(). Learn it for legacy code; build new features with the modern API or Notification.permission.

Continue with getSubscription(), navigator.serviceWorker, PushEvent.data, or the JavaScript hub.

💡 Best Practices

✅ Do

  • Use permissionState() in new projects
  • Feature-detect hasPermission in legacy code
  • Fall back to Notification.permission when appropriate
  • Separate permission checks from subscribe()
  • Update UI for denied vs prompt states

❌ Don’t

  • Call hasPermission() in new production code
  • Assume it exists on all PushManager instances
  • Confuse permission with an existing subscription
  • Ignore MDN’s deprecated warning
  • Ship tutorials that teach hasPermission as current API

Key Takeaways

Knowledge Unlocked

Five things to remember about hasPermission()

Legacy push permission—and what to use instead.

5
Core concepts
🔄02

Promise

async

Returns
🔒03

3 states

granted/denied

Values
🚀04

Replace

permissionState

Modern
🚫05

Non-standard

off spec

Spec

❓ Frequently Asked Questions

It returns a Promise that resolves to a PushPermissionStatus string: "granted", "denied", or "default"—indicating whether the web app has push permission.
MDN marks it Deprecated and Non-standard. It is not Experimental, but it is no longer recommended and has been superseded by permissionState(). Do not use it in new code.
Use PushManager.permissionState() for the modern Push API permission check. For simple notification permission, Notification.permission may also suffice.
No. MDN documents hasPermission() with no parameters. Call it on a PushManager instance: registration.pushManager.hasPermission().
granted means push is allowed; denied means blocked; default means the user has not decided yet (prompt may appear on subscribe).
You may encounter it in legacy tutorials or old codebases. Understanding it helps you migrate safely to permissionState() or Notification.permission.
Did you know?

MDN maps legacy default from hasPermission() to prompt in permissionState()—both mean the user has not made a permission choice yet.

Use the modern API

Learn permissionState()—the recommended replacement for hasPermission().

permissionState() →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful