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

@drb0r1s/use-hash-cancel

v1.0.0

Published

A React hook that syncs modal/popup state with the URL hash, so the browser back button or back gesture closes them instead of navigating away.

Downloads

93

Readme

useHashCancel

A React hook that syncs modal and popup state with the URL hash, so the browser's back button (or a mobile back gesture) closes the modal instead of navigating away from the page.

Why useHashCancel?

Modals and popups that don't affect the URL create a common UX problem: a user opens a modal, hits back (or swipes back on mobile), and instead of the modal closing, the whole page navigates away. useHashCancel fixes this by pushing a hash onto the URL whenever a modal opens, and closing the modal automatically when that hash is popped off the history stack via a back navigation.

Built while developing the frontend for a large student platform, where this pattern needed to work reliably across many independent modals and popups at once.

Installation

npm i use-hash-cancel

react and react-router are required as peer dependencies (react-router v6+).

Usage

Single modal

For a single modal controlled by one boolean state, pass a fixed hash.

import { useState } from "react";
import { useHashCancel } from "use-hash-cancel";

const App = () => {
    const [isModalOpen, setIsModalOpen] = useState(false);

    useHashCancel({
        hash: "modal",
        state: isModalOpen,
        setState: setIsModalOpen
    });

    return (
        <>
            <button onClick={() => setIsModalOpen(true)}>Open modal</button>
            {isModalOpen && <Modal onClose={() => setIsModalOpen(false)} />}
        </>
    );
};

Opening the modal pushes #modal onto the URL. Hitting back removes it and automatically closes the modal via setIsModalOpen(false).

Multiple modals (object state)

For several independent modals, pass an object state instead. Each key becomes its own hash, and no hash prop is needed since the key is tracked from the object itself.

import { useState } from "react";
import { useHashCancel } from "use-hash-cancel";

const App = () => {
    const [modals, setModals] = useState({
        settings: false,
        share: false
    });

    useHashCancel({
        state: modals,
        setState: setModals
    });

    return (
        <>
            <button onClick={() => setModals(prev => ({...prev, settings: true}))}>Open settings</button>
            <button onClick={() => setModals(prev => ({...prev, share: true}))}>Open share</button>

            {modals.settings && <SettingsModal onClose={() => setModals(prev => ({...prev, settings: false}))} />}
            {modals.share && <ShareModal onClose={() => setModals(prev => ({...prev, share: false}))} />}
        </>
    );
};

Opening settings pushes #settings; opening share pushes #share. Hitting back closes whichever one is currently open by setting its key back to false.

How it works

  1. On mount, any existing hash in the URL is stripped, so reloading a page with a stale #modal in the URL doesn't force a modal open on load.
  2. When state changes to an "open" value, the hook pushes the corresponding hash onto the URL via navigate.
  3. When the URL hash changes (most notably when the browser's back action pops it off the history stack), the hook detects that the hash no longer matches the open modal and calls setState to close it, using the same state update the modal would use to close itself normally.
  4. For object state, an internal ref tracks which key currently owns the active hash, so only the correct modal is closed when the hash is removed.

This means back-button and back-gesture navigation cancels the modal instead of leaving the page, without requiring any changes to how the modal itself is closed.

API

useHashCancel({ hash, state, setState })

| Param | Type | Required | Description | |---|---|---|---| | hash | string | Only for boolean state | The hash to sync with, without the #. | | state | boolean | object | Yes | Either a single boolean flag, or an object of named boolean flags for multiple modals. | | setState | function | Yes | The corresponding state setter (useState's setter). |

Requirements

  • React 17+
  • React Router v6+ (react-router)

License

MIT