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

@svnrnns/react-modals

v1.1.0

Published

imperative modal stack manager for React

Downloads

563

Readme

@svnrnns/react-modals

Imperative modal stack manager for React. Open modals imperatively with pushModal(); they stack on screen with an overlay, animations, and focus trap.

Install

npm install @svnrnns/react-modals

Setup

  1. Mount ModalRoot once in your app (e.g. in your root layout).
import { ModalRoot } from "@svnrnns/react-modals";
import "@svnrnns/react-modals/styles.css";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ModalRoot />
        {children}
      </body>
    </html>
  );
}

ModalRoot props:

  • disableBodyScroll – When true, blocks background scrolling while any modal is open using document-level wheel / touchmove listeners (no overflow: hidden on body). Scrolling inside the modal still works. Default: false.
  1. Import and use pushModal, popModal, closeModalById, and closeAllModals anywhere (no context needed).

Usage

Basic modal

import { pushModal } from "@svnrnns/react-modals";

function MyContent({
  name,
  closeModal,
}: {
  name: string;
  closeModal: () => void;
}) {
  return (
    <div>
      <p>Hello, {name}</p>
      <button onClick={closeModal}>Close</button>
    </div>
  );
}

function App() {
  return (
    <button
      onClick={() =>
        pushModal({
          component: MyContent,
          props: { name: "World" },
          title: "Greeting",
        })
      }
    >
      Open modal
    </button>
  );
}

TypeScript infers props from your component, so props: { name: "World" } is type-checked.

Options

  • component – React component to render (receives props + closeModal).
  • props – Props for the component (inferred from component).
  • title – Optional text in the modal header. If you omit it or pass an empty string, the header row and close button still render; the title line keeps normal height for layout. Use hideHeader to remove the whole header.
  • hideHeader – If true, the header (title + close button) is hidden; close via Escape, click outside, or a button in the content.
  • width / height – Optional modal size (e.g. "400px", 400).
  • className – Optional extra classes on the outer modal frame (modals-frame).
  • bodyClassName – Optional extra classes on the body column (modals-body), i.e. the block that contains the close control, header, and content.
  • headerClassName – Optional extra classes on the header strip (modals-header). Ignored when hideHeader is true.
  • titleClassName – Optional extra classes on the title heading (modals-title). Ignored when hideHeader is true.
  • contentClassName – Optional extra classes on the scrollable wrapper around your component (modals-content).

Each *ClassName option is appended after the library’s base class on that element; use a single string with space-separated names (e.g. "my-modal-header text-lg").

  • footer – Optional { component, props?, className? } for a footer component. The footer component receives its props plus closeModal (same as the modal content), so you can add close/cancel buttons in the footer.
  • onClose – Callback when the modal is closed.
  • disableClickOutside – If true, clicking the backdrop does not close.
  • disableEsc – If true, Escape does not close.
  • disableAutoFocus – If true, no element is focused when the modal opens (focus trap for Tab still works).

API

  • pushModal(options) – Pushes a modal onto the stack. Returns the modal id (string). Store it to close that modal later with closeModalById(id).
  • popModal() – Closes the topmost modal.
  • closeModalById(id) – Closes the modal with the given id (the one returned by pushModal).
  • closeAllModals() – Closes all modals.

Each modal content component receives closeModal (no arguments): call it to close that modal (works even if it is not the top one).

Focus trap

When a modal is on top, focus is trapped inside it: Tab / Shift+Tab wrap within the modal, and when the modal closes, focus returns to the previously focused element.

CSS variables

Override these in your app to style modals:

| Variable | Default | Description | | ----------------------------- | ------------------------------------- | ------------------------------------------------------------ | | --modal-bg | #fff | Modal background | | --modal-border | 1px solid transparent | Modal border (width, style, color; e.g. 2px solid #e2e8f0) | | --modal-max-width | min(90vw, 90dvw) | Maximum modal width | | --modal-max-height | min(95vh, 95dvh) | Maximum modal height | | --modal-padding | 1rem | Padding for header and content area | | --modal-footer-padding | var(--modal-padding) | Padding for the footer | | --modal-gap | 1rem | Gap between header, content, footer | | --modal-title-color | #0f172a | Title text color | | --modal-title-font-size | 1.25rem | Title font size | | --modal-title-line-height | 1 | Title line height | | --modal-border-radius | 0.5rem | Modal corners | | --modal-shadow | 0 25px 50px -12px rgb(0 0 0 / 0.25) | Box shadow | | --modal-overlay-bg | rgba(0, 0, 0, 0.3) | Backdrop color | | --modal-overlay-blur-filter | blur(4px) | Backdrop blur (full filter value) | | --modal-dim-filter | brightness(0.85) | Filter for modals behind the top one | | --modal-duration | 150ms | Animation duration | | --modal-close-size | 2rem | Close button width and height | | --modal-close-padding | 0.25rem | Close button padding | | --modal-close-border-radius | 0.25rem | Close button border radius | | --modal-close-bg | transparent | Close button background | | --modal-close-hover-bg | rgba(0, 0, 0, 0.05) | Close button hover background | | --modal-close-color | currentColor | Close icon color | | --modal-close-hover-color | currentColor | Close icon hover color |

Example:

:root {
  --modal-border: 1px solid #e2e8f0;
  --modal-border-radius: 0.75rem;
  --modal-overlay-blur-filter: blur(8px);
}

Requirements

  • React 18 or 19

React Frameworks

Modals are rendered with createPortal into document.body, so they work with Next.js App Router and SSR. This also applies to other React frameworks.