JavaScript PushManager permissionState() Method

Beginner
⏱️ 11 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
Baseline Widely available
Instance method

What You’ll Learn

The permissionState() method on PushManager returns the push permission state as prompt, denied, or granted. Learn the MDN serviceWorker.ready pattern, the userVisibleOnly option, replacing deprecated hasPermission(), and updating subscribe UI—with five examples and try-it labs.

01

Kind

Instance method

02

Returns

Promise

03

Values

prompt / denied

04

Options

userVisibleOnly

05

Replaces

hasPermission

06

Status

Baseline Mar 2023

Introduction

Before calling subscribe(), your PWA should know whether push is allowed, blocked, or not yet decided. The Push API exposes permissionState() on PushManager for that check—the modern, standardized way to read push permission.

MDN marks it Baseline Widely available (since March 2023). It superseded the deprecated hasPermission() method and maps legacy default to prompt. In Firefox, notification and push permissions are merged—granting notifications also enables push.

💡
Permission vs subscription

permissionState() tells you if push is allowed. getSubscription() tells you if the user already has an active PushSubscription. Check both on page load.

Understanding permissionState()

An instance method on PushManager that queries push permission asynchronously for the given subscribe options.

  • Parameters — optional options object.
  • userVisibleOnly — boolean; push messages must be user-visible.
  • applicationServerKey — VAPID public key (same as for subscribe()).
  • ReturnsPromise<string>.
  • granted — push permission allowed.
  • denied — push permission blocked.
  • prompt — user has not decided yet.
  • Available in Web Workers per MDN (secure context).

📝 Syntax

JavaScript
pushManager.permissionState()
pushManager.permissionState(options)

Return value

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

MDN pattern

JavaScript
const registration = await navigator.serviceWorker.ready;

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

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

⚡ Quick Reference

GoalCode / note
Basic checkawait pushManager.permissionState({ userVisibleOnly: true })
With VAPID keyPass applicationServerKey in options
After SW readyawait navigator.serviceWorker.ready
Notification shortcutNotification.permission (sync)
Legacy mappingdefaultprompt
MDN statusBaseline Widely available

🔍 At a Glance

Four facts to remember about permissionState().

Async
Promise

Returns string

Values
3 states

prompt/denied/granted

Options
subscribe

Same as subscribe()

Status
Baseline

Since Mar 2023

Examples Gallery

Examples follow MDN permissionState() patterns. Use View Output or Try It Yourself.

📚 Getting Started

Read push permission after serviceWorker.ready.

Example 1 — Basic permissionState() Check

Query permission with userVisibleOnly: true.

JavaScript
navigator.serviceWorker.ready.then((registration) => {
  return registration.pushManager.permissionState({
    userVisibleOnly: true,
  });
}).then((state) => {
  console.log(state); // "prompt" | "denied" | "granted"
});
Try It Yourself

How It Works

Always wait for serviceWorker.ready before accessing pushManager.

Example 2 — Branch UI on Permission State

Update button text for prompt, denied, or granted.

JavaScript
const state = await registration.pushManager.permissionState({
  userVisibleOnly: true,
});
const btn = document.querySelector(".js-push-button");

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

How It Works

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

📈 Practical Patterns

async/await, VAPID keys, and sync alternatives.

Example 3 — async/await Helper

Reusable function for page-load permission checks.

JavaScript
async function getPushPermissionState() {
  const registration = await navigator.serviceWorker.ready;
  return registration.pushManager.permissionState({
    userVisibleOnly: true,
  });
}

const state = await getPushPermissionState();
console.log("Push permission:", state);
Try It Yourself

How It Works

Wrap in try/catch for secure-context and service-worker errors.

Example 4 — With applicationServerKey

Pass the same VAPID key you use in subscribe().

JavaScript
// Convert URL-safe base64 VAPID public key to Uint8Array
function urlBase64ToUint8Array(base64String) {
  const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
  const raw = atob(base64);
  return Uint8Array.from([...raw].map((c) => c.charCodeAt(0)));
}

const vapidPublicKey = "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U";
const applicationServerKey = urlBase64ToUint8Array(vapidPublicKey);

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

How It Works

MDN: pass the same options object you plan to use when subscribing.

Example 5 — Compare with Notification.permission

Synchronous shortcut when push always shows notifications.

JavaScript
// Sync — maps "default" to permissionState's "prompt"
const notifPerm = Notification.permission;

if (notifPerm === "granted") {
  console.log("Notifications allowed (push likely enabled in Firefox)");
} else if (notifPerm === "denied") {
  console.log("Blocked");
} else {
  console.log("Not decided yet (like permissionState prompt)");
}
Try It Yourself

How It Works

Firefox merged notification and push permissions per MDN. Prefer permissionState() for accuracy.

🚀 Common Use Cases

  • Showing or hiding an “Enable Push” button on page load.
  • Disabling subscribe UI when permission is denied.
  • Checking permission before calling subscribe().
  • Migrating from deprecated hasPermission() to the modern API.
  • Pairing with getSubscription() to restore full push state.
  • Validating VAPID key options before subscription setup.

🔧 How It Works

1

SW ready

Get registration.pushManager.

Setup
2

Call permissionState

Pass subscribe options (e.g. userVisibleOnly).

Query
3

Promise resolves

prompt, denied, or granted.

Status
4

Update app

Toggle UI or call subscribe() when appropriate.

📝 Notes

Universal Browser Support

The PushManager.permissionState() method is Baseline Widely available on MDN (since March 2023). Requires a secure context. Logos use the shared browser-image-sprite.png sprite from this project.

Baseline · Widely available

PushManager.permissionState()

Check push permission as prompt, denied, or granted in modern browsers.

Universal Widely available
Google Chrome Full support · Desktop & Mobile
Full support
Mozilla Firefox Full support · Desktop & Mobile
Full support
Apple Safari Full support · macOS & iOS
Full support
Microsoft Edge Full support · Chromium
Full support
Opera Full support · Modern versions
Full support
Internet Explorer No Push API / service workers
Not supported
permissionState() Excellent

Bottom line: Call after serviceWorker.ready with the same options you use for subscribe().

Conclusion

pushManager.permissionState() is the modern way to read push permission as prompt, denied, or granted. Call it after serviceWorker.ready with the same options you use for subscribe()—then update your UI or proceed to subscription.

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

💡 Best Practices

✅ Do

  • Use permissionState() in new push projects
  • Pass the same options as subscribe()
  • Wait for navigator.serviceWorker.ready first
  • Handle denied with clear UI messaging
  • Pair with getSubscription() on page load

❌ Don’t

  • Use deprecated hasPermission() in new code
  • Confuse permission with an existing subscription
  • Call before the service worker is ready
  • Expect it to work outside a secure context
  • Assume Notification.permission always matches push

Key Takeaways

Knowledge Unlocked

Five things to remember about permissionState()

Modern push permission checks made simple.

5
Core concepts
🔄02

Promise

async

Returns
🔒03

3 states

prompt/denied

Values
⚙️04

Options

userVisibleOnly

Config
🎯05

Baseline

since Mar 2023

Status

❓ Frequently Asked Questions

It returns a Promise that resolves to a permission string: "prompt", "denied", or "granted"—indicating whether push messages are allowed for the given options.
No. MDN marks PushManager.permissionState() as Baseline Widely available (since March 2023). It is not Deprecated, Experimental, or Non-standard.
An optional object with userVisibleOnly (boolean) and applicationServerKey (VAPID public key). Pass the same options you plan to use with subscribe().
granted means push is allowed; denied means blocked; prompt means the user has not decided yet (a permission dialog may appear when subscribing).
permissionState() is the modern, standardized replacement. It uses "prompt" instead of legacy "default" and accepts subscribe options like userVisibleOnly.
On registration.pushManager after navigator.serviceWorker.ready, in a secure context (HTTPS or localhost). Also available in Web Workers per MDN.
Did you know?

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

Register service workers

Learn navigator.serviceWorker—needed before any PushManager method.

serviceWorker →

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