# react-responsive-modal keeps the accessibility work inside a small package

> react-responsive-modal is a small React modal that ships the parts people usually get wrong: a focus trap, centred and scrolling layouts, support for stacking several modals, and props for customising the behaviour. The usage example is short enough to be the entire API surface most applications need, and the package measures its own bundle size.

**pradel/react-responsive-modal** — Simple responsive react modal

- Repository: https://github.com/pradel/react-responsive-modal
- Website: https://react-responsive-modal.leopradel.com/
- Stars: 611 · Forks: 99
- Language: TypeScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/pradel-react-responsive-modal

## The feature list is a list of things that are easy to get wrong

The README describes the library as a simple responsive and accessible react modal, and then lists what that means in practice.

There is a focus trap inside the modal. There are centred modals. There are scrolling modals. There is support for multiple modals. There are accessible modals. There is easy customisation via props. There is TypeScript support. And there is a small bundle size, with a link to bundlephobia for the measurement.

Almost every entry in that list is a thing you would otherwise implement yourself, badly. A focus trap is the piece that keeps a keyboard user inside the dialog instead of tabbing into the page behind it, and getting that right requires listening for the focus boundary, handling shift-tab at the top edge, and restoring focus to the trigger when the dialog closes. That is a day of work for one component, and it is the part that is hardest to test.

Multiple modals is the other entry that is more than it sounds. Stacking dialogs means the focus trap has to know which dialog is on top, and the escape key handling has to close the right one.

The bundle size entry is the other one worth noting. A modal that brings in a headless UI library, a portal manager and a transition system can be tens of kilobytes for something the user sees for a second. This one advertises being small, and the bundlephobia link lets you check the current figure rather than trusting the claim.

The project is MIT licensed by Léo Pradel, with the last push on 2026-10-16 and releases v8.0.0 from 2026-09-25, v7.2.0 from 2026-06-13 and v7.1.0 from 2025-10-01.

## The usage example shows a controlled component, not a stateful one

The usage example is a CodeSandbox link plus a short block of code, and the code is worth reading carefully because it establishes the component's contract.

```javascript
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import 'react-responsive-modal/styles.css';
import { Modal } from 'react-responsive-modal';

const App = () => {
  const [open, setOpen] = useState(false);

  const onOpenModal = () => setOpen(true);
  const onCloseModal = () => setOpen(false);

  return (
    <div>
      <button onClick={onOpenModal}>Open modal</button>
      <Modal open={open} onClose={onCloseModal} center>
        <h2>Simple centered modal</h2>
      </Modal>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('app'));
```

Two things stand out. The first is that open and onClose are passed in, so the modal is a controlled component. The state lives in your App, and the modal calls your handler. That means the modal does not own the notion of whether it is open, which is what lets you close it from a button inside it, from a link, or from a timeout.

The second is the stylesheet import. The component ships its own CSS, and it has to be imported separately:

```javascript
import 'react-responsive-modal/styles.css';
```

That is the one piece of setup that is easy to miss and produces a modal with no styling at all, which then looks like the library is broken rather than like a missing import.

The `center` prop is the other visible thing in the example, and it is what makes the dialog sit in the middle of the viewport rather than near the top. That is a prop rather than a separate component, which is the shape the whole library takes: one component with many props rather than a family of variants.

The example also uses ReactDOM.render directly rather than createRoot, which suggests the snippet has not been updated for the React 18 rendering change. Worth knowing if you copy it literally, since it will warn in a current React app.

## The props are the real API, and they live in the documentation

The README does not list the props. It points at the documentation site and says to check the props section there.

That is a reasonable division of labour. The README is an overview with one example, and the props are a large surface that would swamp it. The documentation site at react-responsive-modal.leopradel.com is organised into getting started, installation, usage, props and licence, and the props page is the reference.

The structure of the library tells you what to expect from that reference. Since everything is a prop, the props are grouped by concern, and the feature list maps onto them. The focus trap and the accessibility behaviour are options you can turn on or configure. The centred and scrolling layouts are modes. Multiple modals are enabled rather than assumed. And customisation runs through props for both behaviour and appearance, with the stylesheet import giving you the base and the props letting you adjust it.

The TypeScript support matters here more than it does in most libraries of this size, because the props are the API and having them typed means an incorrect prop is a compile error rather than a silent no-op. A prop that the component ignores is a frustrating thing to debug, and a typed props interface turns that into a build failure.

The project also maintains a documentation site in the repository, with a website/ directory at the top level and a netlify.toml, so the docs are versioned alongside the code rather than living somewhere separate.

## The build has moved to a workspace with a monorepo tool

The root package file describes a workspace root rather than the library itself, and that is worth understanding before you contribute rather than consume.

The root package is marked private, and its scripts delegate to a workspace runner. The ready script runs the formatter, then a recursive build, then the linter, then a recursive test run. The build, test and format scripts are each thin wrappers over the same runner, and prepare runs the configuration step.

The tool is vite-plus, invoked as vp, with vite at a catalog version, which is a catalog dependency rather than a fixed version, meaning the version comes from a workspace-level catalog definition.

The devEngines block pins the runtime. Node 26.10.0 is required, with onFail set to download, so a machine without that Node version is given the option to fetch it rather than failing. The package manager is pnpm at 12.5.1, and there is a pnpm-workspace.yaml and a pnpm-lock.yaml at the root.

The library itself lives in its own react-responsive-modal/ directory, and there is a vite.config.ts at the root for the build, plus a .vite-hooks/ directory.

The release process is automated in a different way. There is a release-please-config.json and a .release-please-manifest.json, and the release tags are named react-responsive-modal-v8.0.0, react-responsive-modal-v7.2.0 and react-responsive-modal-v7.1.0, with a scope prefix on the package name. Release Please derives the version bump and the changelog from conventional commits, which is why the tags carry the package scope.

There is also an AGENTS.md at the root, so the project expects contributions to be made with tooling assistance rather than by hand.

## Where this sits against the alternatives

The comparison is with the other two common approaches, and each loses to this library in a different situation.

The first is a headless UI library such as Radix or Headless UI. Those give you accessible primitives, but a dialog is only one of the primitives, so you adopt a dependency for a package that also covers menus, popovers, selects and tabs. If you already use that library, you should use its dialog.

The second is the native HTML dialog element, which is now reasonably well supported and gives you a modal dialog with the browser handling the top layer and the backdrop. Its limitation is that the API is imperative, so you drive it with a ref and an open call rather than with a prop, and the styling hooks are limited to what the browser exposes. A React library that wraps it gives you the declarative shape and the styling freedom.

The third is writing your own, which is what many codebases do once and then live with. It is around a hundred lines for a basic version, and then it needs the focus trap, the escape handling, the scroll lock, the backdrop click behaviour, the body scroll compensation for the scrollbar disappearing, the portal to escape overflow containers, and the focus restoration on close. Each of those is a small amount of work, and each of those is a source of accessibility bugs.

This library's position is the middle one. It is a single component with a large prop surface, it handles the parts that are easy to get wrong, and it does not try to be a dialog system for a whole application.

The accessibility claim is the one to test yourself rather than take on trust. Focus trapping and keyboard support are the properties to check against your own content, because a modal can trap focus perfectly and still be unusable if the content inside it is not navigable. The documentation and the example are the starting point, and the codecov badge suggests the behaviour is at least partly covered by tests.

## Conclusion

Adopt react-responsive-modal if you need a modal in React and want the focus trap and the accessibility behaviour handled rather than reimplemented, since that is the part most hand-rolled modals get wrong. Skip it if you need a dialog system with focus management across a whole application, because this is a single modal component rather than a dialog manager, and if you are on an old React version, since the release line has moved forward quickly. Verify first which stylesheet import you need, since the component ships its own CSS that has to be imported separately, that you understand the open and onClose props are controlled rather than internal, and that you check the accessibility notes in the documentation before shipping, because the component handles focus but your content still has to be navigable.

## FAQ

### How do I install and use react-responsive-modal?

Install it with `npm install react-responsive-modal --save` or `yarn add react-responsive-modal`, then import the component and its stylesheet with `import { Modal } from 'react-responsive-modal'` and `import 'react-responsive-modal/styles.css'`. The modal is controlled, so you pass an open boolean and an onClose handler, and the center prop centres it in the viewport.

### What does react-responsive-modal handle for accessibility?

The library ships a focus trap inside the modal, support for accessible modals, and handling for multiple modals, centred modals and scrolling modals. These are the parts that are easy to implement incorrectly in a hand-rolled modal, and they are the reason the README lists them as features rather than treating them as an implementation detail.

### Is react-responsive-modal still maintained and how is it released?

Yes. The last push was on 2026-10-16 and recent releases are v8.0.0 from 2026-09-25, v7.2.0 from 2026-06-13 and v7.1.0 from 2025-10-01. Releases use release-please with a scoped tag name, the repository uses a pnpm workspace with vite-plus as the task runner, and Node 26.10.0 is the pinned runtime in devEngines.

## Sources

- [License: MIT](https://github.com/pradel/react-responsive-modal/blob/master/LICENSE)
- [pradel/react-responsive-modal on GitHub](https://github.com/pradel/react-responsive-modal)
- [Project website](https://react-responsive-modal.leopradel.com/)
- [README](https://github.com/pradel/react-responsive-modal/blob/master/README.md)
- [Releases](https://github.com/pradel/react-responsive-modal/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pradel-react-responsive-modal
