npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@usefy/use-permission

v0.25.1

Published

A React hook for reading Permissions API status with live updates, SSR-safe and typed

Readme


Overview

usePermission is part of the @usefy ecosystem — a collection of production-ready, TypeScript-first, SSR-safe React hooks. It reads the Permissions API status of a permission and keeps it up to date live, so your UI reacts when the user grants or revokes access.

Features

  • Live updates — subscribes to the PermissionStatus change event, so state reflects grant/revoke without polling or a re-mount.
  • Rich, honest return{ state, status, isSupported, error } makes the async, unsupported, and error paths explicit instead of hiding them behind a bare PermissionState.
  • Any permission name — accepts a superset of PermissionDescriptor, so camera, microphone, push, midi, and future names all typecheck.
  • Stable descriptor identity — keyed on the descriptor's serialized contents, so an inline { name: 'camera' } literal does not re-query every render (no caller-side useMemo).
  • SSR-safe & StrictMode-safe — reports unsupported on the server; the async query is race-guarded and the change listener is cleaned up on unmount.
  • TypeScript-first — full type inference and exported types.
  • Tiny & tree-shakeable — zero dependencies, published as its own package.

Installation

# npm
npm install @usefy/use-permission

# yarn
yarn add @usefy/use-permission

# pnpm
pnpm add @usefy/use-permission

Requires React 18 or 19 (peerDependencies: "react": "^18.0.0 || ^19.0.0").

Quick Start

import { usePermission } from "@usefy/use-permission";

function CameraStatus() {
  const { state, status } = usePermission({ name: "camera" });

  // Branch on `status` (deterministic "idle" on the first render, both on the
  // server and client) so the UI is hydration-safe.
  if (status === "idle" || status === "pending") return <span>Checking…</span>;
  if (status === "unsupported") return <span>Permissions API unavailable</span>;
  if (status === "error") return <span>Could not read camera permission</span>;

  return <span>Camera: {state}</span>; // 'granted' | 'denied' | 'prompt'
}

API

usePermission(descriptor)

function usePermission(descriptor: UsePermissionDescriptor): UsePermissionReturn;

Parameters

| Param | Type | Description | | ----- | ---- | ----------- | | descriptor | UsePermissionDescriptor | The permission to query, e.g. { name: 'camera' }, { name: 'geolocation' }, { name: 'push', userVisibleOnly: true }, { name: 'midi', sysex: true }. name accepts the standard PermissionName values (with autocomplete) plus any browser-specific string. Pass an inline literal freely — the hook keys re-queries on the descriptor's serialized contents, not its object identity. |

Returns — UsePermissionReturn

| Field | Type | Description | | ----- | ---- | ----------- | | state | PermissionState \| null | The raw permission state ('granted' \| 'denied' \| 'prompt'), or null until the first query resolves and whenever the API is unsupported or the query errored. | | status | UsePermissionStatus | Coarse lifecycle: 'idle' \| 'pending' \| 'granted' \| 'denied' \| 'prompt' \| 'unsupported' \| 'error'. The three permission values mirror state, so you can branch on either. | | isSupported | boolean | Whether navigator.permissions.query is available. Starts false (on the server and the first client render, for hydration safety) and becomes true after mount in a supporting browser. Branch your first render on status ('idle'), not isSupported. | | error | Error \| null | The error thrown by navigator.permissions.query(), if the query rejected (e.g. an unknown permission name in a browser that throws). |

Helpers

  • isPermissionsSupported(): boolean — capability check used internally; exported for feature-detection.
  • serializeDescriptor(descriptor): string — the stable-key function the hook uses to decide when to re-query (exported for advanced use; not part of the umbrella surface).

Notes on behavior

  • Descriptor identity. usePermission({ name: 'camera' }) receives a fresh object literal every render. Rather than require callers to useMemo it, the hook derives a stable string key from the descriptor's own fields (sorted, JSON-serialized) and keys its effect on that. It re-queries only when the meaningful contents change, e.g. name 'camera''microphone' or userVisibleOnly truefalse.
  • Unsupported. If navigator.permissions is missing (SSR or an unsupporting browser), status is 'unsupported', isSupported is false, and state is null.
  • Errors. Some browsers reject query() for permission names they don't recognize (e.g. camera in Firefox) instead of returning 'denied'. That surfaces as status: 'error' with the thrown error.
  • Race safety. If the component unmounts while a query is in flight, the stale resolution/rejection is ignored — no state update after unmount — and the change listener is always removed on cleanup.

Testing

📊 View Detailed Coverage Report (GitHub Pages) — 18 tests, 100% statement coverage.

License

MIT © mirunamu

This package is part of the usefy monorepo.