{"id":24911430,"url":"https://github.com/accessible-ui/modal","last_synced_at":"2025-06-19T08:11:23.433Z","repository":{"id":40795247,"uuid":"227722978","full_name":"accessible-ui/modal","owner":"accessible-ui","description":"🅰 An accessible and versatile modal component for React","archived":false,"fork":false,"pushed_at":"2023-01-06T02:20:55.000Z","size":3373,"stargazers_count":16,"open_issues_count":17,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-25T08:19:29.216Z","etag":null,"topics":["a11y","a11y-dialog","a11y-modal","accessibility","accessible-modal","aria","aria-modal","modal","react-a11y","react-a11y-modal","react-aria-modal","react-modal"],"latest_commit_sha":null,"homepage":"https://codesandbox.io/s/accessiblemodal-example-v4koo","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/accessible-ui.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-13T00:37:11.000Z","updated_at":"2023-11-27T10:27:04.000Z","dependencies_parsed_at":"2023-02-05T02:46:26.353Z","dependency_job_id":null,"html_url":"https://github.com/accessible-ui/modal","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/accessible-ui/modal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/accessible-ui%2Fmodal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/accessible-ui%2Fmodal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/accessible-ui%2Fmodal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/accessible-ui%2Fmodal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/accessible-ui","download_url":"https://codeload.github.com/accessible-ui/modal/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/accessible-ui%2Fmodal/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260713353,"owners_count":23050827,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["a11y","a11y-dialog","a11y-modal","accessibility","accessible-modal","aria","aria-modal","modal","react-a11y","react-a11y-modal","react-aria-modal","react-modal"],"created_at":"2025-02-02T04:20:06.891Z","updated_at":"2025-06-19T08:11:18.420Z","avatar_url":"https://github.com/accessible-ui.png","language":"TypeScript","readme":"\u003chr\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ch1 align=\"center\"\u003e\n    \u0026lt;Modal\u0026gt;\n  \u003c/h1\u003e\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://bundlephobia.com/result?p=@accessible/modal\"\u003e\n    \u003cimg alt=\"Bundlephobia\" src=\"https://img.shields.io/bundlephobia/minzip/@accessible/modal?style=for-the-badge\u0026labelColor=24292e\"\u003e\n  \u003c/a\u003e\n  \u003ca aria-label=\"Types\" href=\"https://www.npmjs.com/package/@accessible/modal\"\u003e\n    \u003cimg alt=\"Types\" src=\"https://img.shields.io/npm/types/@accessible/modal?style=for-the-badge\u0026labelColor=24292e\"\u003e\n  \u003c/a\u003e\n  \u003ca aria-label=\"Code coverage report\" href=\"https://codecov.io/gh/accessible-ui/modal\"\u003e\n    \u003cimg alt=\"Code coverage\" src=\"https://img.shields.io/codecov/c/gh/accessible-ui/modal?style=for-the-badge\u0026labelColor=24292e\"\u003e\n  \u003c/a\u003e\n  \u003ca aria-label=\"Build status\" href=\"https://travis-ci.org/accessible-ui/modal\"\u003e\n    \u003cimg alt=\"Build status\" src=\"https://img.shields.io/travis/accessible-ui/modal?style=for-the-badge\u0026labelColor=24292e\"\u003e\n  \u003c/a\u003e\n  \u003ca aria-label=\"NPM version\" href=\"https://www.npmjs.com/package/@accessible/modal\"\u003e\n    \u003cimg alt=\"NPM Version\" src=\"https://img.shields.io/npm/v/@accessible/modal?style=for-the-badge\u0026labelColor=24292e\"\u003e\n  \u003c/a\u003e\n  \u003ca aria-label=\"License\" href=\"https://jaredlunde.mit-license.org/\"\u003e\n    \u003cimg alt=\"MIT License\" src=\"https://img.shields.io/npm/l/@accessible/modal?style=for-the-badge\u0026labelColor=24292e\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cpre align=\"center\"\u003enpm i @accessible/modal\u003c/pre\u003e\n\u003chr\u003e\n\nAn accessible and versatile modal component for React\n\n## Features\n\n- [x] **Style-agnostic** You can use this component with the styling library of your choice. It\n      works with CSS-in-JS, SASS, plain CSS, plain `style` objects, anything!\n- [x] **Portal-friendly** The modal target will render into React portals of your choice when configured\n      to do so.\n- [x] **a11y/aria-compliant** This component works with screen readers out of the box and manages\n      focus for you.\n\n## Quick Start\n\n[Check out the example on **CodeSandbox**](https://codesandbox.io/s/accessiblemodal-example-v4koo)\n\n```jsx harmony\nimport * as React from 'react'\nimport * as Modal from '@accessible/modal'\n\nconst Component = () =\u003e (\n  \u003cModal.Modal\u003e\n    \u003cModal.Trigger\u003e\n      \u003cbutton\u003eOpen me\u003c/button\u003e\n    \u003c/Modal.Trigger\u003e\n\n    \u003cModal.Target\u003e\n      \u003cdiv className='my-modal'\u003e\n        \u003cModal.CloseButton\u003e\n          \u003cbutton\u003eClose me\u003c/button\u003e\n        \u003c/Modal.CloseButton\u003e\n\n        \u003cdiv\u003eYou did a thing\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/Modal.Target\u003e\n  \u003c/Modal.Modal\u003e\n)\n```\n\n## API\n\n### Components\n\n| Component                       | Description                                                                                                   |\n| ------------------------------- | ------------------------------------------------------------------------------------------------------------- |\n| [`\u003cModal\u003e`](#modal)             | This component creates the context for your modal target and trigger and contains some configuration options. |\n| [`\u003cTarget\u003e`](#target)           | This component wraps any React element and turns it into a modal target.                                      |\n| [`\u003cTrigger\u003e`](#trigger)         | This component wraps any React element and turns it into a modal trigger.                                     |\n| [`\u003cCloseButton\u003e`](#closebutton) | This is a convenience component that wraps any React element and adds an onClick handler to close the modal.  |  |\n\n### Hooks\n\n| Hook                                                        | Description                                                                                                                                                       |\n| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [`useModal()`](#usemodal)                                   | This hook provides the value of the modal's [ModalContextValue object](#modalcontextvalue).                                                                       |\n| [`useA11yTarget()`](#usea11ytargettarget-options)           | A React hook for creating a headless modal target to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html).  |\n| [`useA11yTrigger()`](#usea11ytriggertarget-options)         | A React hook for creating a headless modal trigger to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html). |\n| [`useA11yCloseButton()`](#usea11yclosebuttontarget-options) | A React hook for creating a headless close button to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html).  |\n\n### \u0026lt;Modal\u0026gt;\n\nThis component creates the context for your modal target and trigger and contains some\nconfiguration options.\n\n#### Props\n\n| Prop        | Type                      | Default     | Required? | Description                                                                                                                                               |\n| ----------- | ------------------------- | ----------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| defaultOpen | `boolean`                 | `false`     | No        | This sets the default open state of the modal. By default the modal is closed.                                                                            |\n| open        | `boolean`                 | `undefined` | No        | This creates a controlled modal component where the open state of the modal is controlled by this property.                                               |\n| onChange    | `(open: boolean) =\u003e void` | `undefined` | No        | This callback is invoked any time the `open` state of the modal changes.                                                                                  |\n| id          | `string`                  | `undefined` | No        | By default this component creates a unique id for you, as it is required for certain aria attributes. Supplying an id here overrides the auto id feature. |\n| children    | `React.ReactNode`         | `undefined` | No        | Your modal contents and any other children.                                                                                                               |\n\n### useA11yTarget(target, options?)\n\nA React hook for creating a headless modal target to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html).\n\n#### Arguments\n\n| Argument | Type                                               | Required? | Description                 |\n| -------- | -------------------------------------------------- | --------- | --------------------------- |\n| target   | \u003ccode\u003eReact.RefObject\u0026lt;T\u0026gt; \\| T \\| null\u003c/code\u003e | Yes       | A React ref or HTML element |\n| options  | [`UseA11yTargetOptions`](#usea11ytargetoptions)    | No        | Configuration options       |\n\n#### UseA11yTargetOptions\n\n```ts\nexport interface UseA11yTargetOptions {\n  /**\n   * Adds this class name to props when the modal is open\n   */\n  openClass?: string\n  /**\n   * Adds this class name to props when the modal is closed\n   */\n  closedClass?: string\n  /**\n   * Adds this style to props when the modal is open\n   */\n  openStyle?: React.CSSProperties\n  /**\n   * Adds this style to props when the modal is closed\n   */\n  closedStyle?: React.CSSProperties\n  /**\n   * Prevents the `window` from scrolling when the target is\n   * focused after opening.\n   */\n  preventScroll?: boolean\n  /**\n   * When `true`, this closes the target element when the `Escape`\n   * key is pressed.\n   * @default true\n   */\n  closeOnEscape?: boolean\n}\n```\n\n#### Returns\n\n```ts\ninterface A11yProps {\n  readonly role: 'dialog'\n  readonly 'aria-hidden': boolean\n  readonly id: string | undefined\n  readonly className: string | undefined\n  readonly style: {\n    readonly visibility: 'hidden' | 'visible'\n    readonly position: 'fixed'\n    readonly margin: 'auto'\n    readonly left: '50%'\n    readonly top: '50%'\n    readonly transform: 'translate3d(-50%, -50%, 0)'\n  }\n}\n```\n\n#### Example\n\n```jsx harmony\nimport * as React from 'react'\nimport {useA11yTarget} from '@accessible/modal'\n\nconst MyTarget = () =\u003e {\n  const ref = React.useRef(null)\n  const a11yProps = useA11yTarget(ref, {closeOnEscape: false})\n\n  return (\n    \u003cdiv ref={ref} {...a11yProps}\u003e\n      I am the modal dialog\n    \u003c/div\u003e\n  )\n}\n```\n\n### \u0026lt;Target\u0026gt;\n\nThis component wraps any React element and turns it into a modal target.\n\n#### Props\n\n| Prop          | Type                                              | Default     | Required? | Description                                                                                                                                                                                                    |\n| ------------- | ------------------------------------------------- | ----------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| portal        | \u003ccode\u003eboolean \\| string \\| PortalizeProps \u003c/code\u003e | `false`     | No        | When `true` this will render the modal into a React portal with the id `#portals`. You can render it into any portal by providing its query selector here, e.g. `#foobar`, `[data-portal=true]`, or `.foobar`. |\n| closeOnEscape | `boolean`                                         | `true`      | No        | By default the modal will close when the `Escape` key is pressed. You can turn this off by providing `false` here.                                                                                             |\n| closedClass   | `string`                                          | `undefined` | No        | This class name will be applied to the child element when the modal is `closed`.                                                                                                                               |\n| openClass     | `string`                                          | `undefined` | No        | This class name will be applied to the child element when the modal is `open`.                                                                                                                                 |\n| closedStyle   | `React.CSSProperties`                             | `undefined` | No        | These styles will be applied to the child element when the modal is `closed` in addition to the default styles that set the target's visibility.                                                               |\n| openStyle     | `React.CSSProperties`                             | `undefined` | No        | These styles name will be applied to the child element when the modal is `open` in addition to the default styles that set the target's visibility.                                                            |\n| preventScroll | `boolean`                                         | `false`     | No        | When `true` this will prevent your browser from scrolling the document to bring the newly-focused tab into view.                                                                                               |\n| children      | `React.ReactElement`                              | `undefined` | Yes       | The child is cloned by this component and has aria attributes injected into its props as well as the events defined above.                                                                                     |\n\n#### Example\n\n```jsx harmony\n\u003cTarget\u003e\n  \u003cdiv className='alert'\u003eAlert\u003c/div\u003e\n\u003c/Target\u003e\n```\n\n### useA11yTrigger(target, options?)\n\nA React hook for creating a headless modal trigger to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html).\nIn addition to providing accessibility props to your component, this hook will add events\nfor interoperability between actual \u003cbutton\u003e elements and fake ones e.g. \u003ca\u003e and \u003cdiv\u003e to the\ntarget element\n\n#### Arguments\n\n| Argument | Type                                               | Required? | Description                 |\n| -------- | -------------------------------------------------- | --------- | --------------------------- |\n| target   | \u003ccode\u003eReact.RefObject\u0026lt;T\u0026gt; \\| T \\| null\u003c/code\u003e | Yes       | A React ref or HTML element |\n| options  | [`UseA11yTriggerOptions`](#usea11ytriggeroptions)  | No        | Configuration options       |\n\n#### UseA11yTriggerOptions\n\n```ts\nexport interface UseA11yTriggerOptions {\n  /**\n   * Adds this class name to props when the modal is open\n   */\n  openClass?: string\n  /**\n   * Adds this class name to props when the modal is closed\n   */\n  closedClass?: string\n  /**\n   * Adds this style to props when the modal is open\n   */\n  openStyle?: React.CSSProperties\n  /**\n   * Adds this style to props when the modal is closed\n   */\n  closedStyle?: React.CSSProperties\n  /**\n   * Adds an onClick handler in addition to the default one that\n   * toggles the modal's open state.\n   */\n  onClick?: (e: MouseEvent) =\u003e any\n}\n```\n\n#### Returns\n\n```ts\ninterface A11yProps\u003cE extends React.MouseEvent\u003cany, MouseEvent\u003e\u003e {\n  readonly 'aria-haspopup': 'dialog'\n  readonly 'aria-controls': string | undefined\n  readonly 'aria-expanded': boolean\n  readonly role: 'button'\n  readonly tabIndex: 0\n  readonly className: string | undefined\n  readonly style: React.CSSProperties | undefined\n}\n```\n\n#### Example\n\n```jsx harmony\nimport * as React from 'react'\nimport {useA11yTrigger} from '@accessible/modal'\n\nconst MyTrigger = () =\u003e {\n  const ref = React.useRef(null)\n  const a11yProps = useA11yTrigger(ref, {\n    openClass: 'open',\n    closedClass: 'closed',\n  })\n\n  return (\n    \u003cbutton ref={ref} {...a11yProps}\u003e\n      Clicking me toggles the modal dialog\n    \u003c/button\u003e\n  )\n}\n```\n\n### \u0026lt;Trigger\u0026gt;\n\nThis component wraps any React element and adds an `onClick` handler which toggles the open state\nof the modal target.\n\n#### Props\n\n| Prop        | Type                  | Default     | Required? | Description                                                                                                                |\n| ----------- | --------------------- | ----------- | --------- | -------------------------------------------------------------------------------------------------------------------------- |\n| closedClass | `string`              | `undefined` | No        | This class name will be applied to the child element when the modal is `closed`.                                           |\n| openClass   | `string`              | `undefined` | No        | This class name will be applied to the child element when the modal is `open`.                                             |\n| closedStyle | `React.CSSProperties` | `undefined` | No        | These styles will be applied to the child element when the modal is `closed`.                                              |\n| openStyle   | `React.CSSProperties` | `undefined` | No        | These styles name will be applied to the child element when the modal is `open`.                                           |\n| children    | `React.ReactElement`  | `undefined` | Yes       | The child is cloned by this component and has aria attributes injected into its props as well as the events defined above. |\n\n```jsx harmony\n\u003cTrigger on='click'\u003e\n  \u003cbutton className='my-button'\u003eOpen me!\u003c/button\u003e\n\u003c/Trigger\u003e\n```\n\n### useA11yCloseButton(target, options?)\n\nA React hook for creating a headless close button to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html).\nIn addition to providing accessibility props to your component, this hook will add events\nfor interoperability between actual \u003cbutton\u003e elements and fake ones e.g. \u003ca\u003e and \u003cdiv\u003e to the\ntarget element\n\n#### Arguments\n\n| Argument | Type                                                      | Required? | Description                 |\n| -------- | --------------------------------------------------------- | --------- | --------------------------- |\n| target   | \u003ccode\u003eReact.RefObject\u0026lt;T\u0026gt; \\| T \\| null\u003c/code\u003e        | Yes       | A React ref or HTML element |\n| options  | [`UseA11yCloseButtonOptions`](#usea11yclosebuttonoptions) | No        | Configuration options       |\n\n#### UseA11yCloseButtonOptions\n\n```ts\nexport interface UseA11yCloseButtonOptions {\n  /**\n   * Adds an onClick handler in addition to the default one that\n   * closes the modal.\n   */\n  onClick?: (e: MouseEvent) =\u003e any\n}\n```\n\n#### Returns\n\n```ts\ninterface A11yProps\u003cE extends React.MouseEvent\u003cany, MouseEvent\u003e\u003e {\n  readonly 'aria-haspopup': 'dialog'\n  readonly 'aria-controls': string | undefined\n  readonly 'aria-expanded': boolean\n  readonly 'aria-label': 'Close'\n  readonly role: 'button'\n  readonly tabIndex: 0\n}\n```\n\n#### Example\n\n```jsx harmony\nimport * as React from 'react'\nimport {useA11yCloseButton} from '@accessible/modal'\n\nconst MyTrigger = () =\u003e {\n  const ref = React.useRef(null)\n  const a11yProps = useA11yCloseButton(ref, {\n    onClick: () =\u003e console.log('Closing!'),\n  })\n\n  return (\n    \u003cbutton ref={ref} {...a11yProps}\u003e\n      Clicking me closes the modal dialog\n    \u003c/button\u003e\n  )\n}\n```\n\n### \u0026lt;CloseButton\u0026gt;\n\nThis is a convenience component that wraps any React element and adds an onClick handler which closes the modal.\n\n#### Props\n\n| Prop     | Type                 | Default     | Required? | Description                                                                                                                |\n| -------- | -------------------- | ----------- | --------- | -------------------------------------------------------------------------------------------------------------------------- |\n| children | `React.ReactElement` | `undefined` | Yes       | The child is cloned by this component and has aria attributes injected into its props as well as the events defined above. |\n\n```jsx harmony\n\u003cCloseButton\u003e\n  \u003cbutton className='my-button'\u003eClose me\u003c/button\u003e\n\u003c/CloseButton\u003e\n```\n\n### useModal()\n\nThis hook provides the value of the modal's [ModalContextValue object](#modalcontextvalue)\n\n### ModalContextValue\n\n```typescript\nexport interface ModalContextValue {\n  /**\n   * The open state of the modal\n   */\n  isOpen: boolean\n  /**\n   * Opens the modal\n   */\n  open: () =\u003e void\n  /**\n   * Closes the modal\n   */\n  close: () =\u003e void\n  /**\n   * Toggles the open state of the modal\n   */\n  toggle: () =\u003e void\n  /**\n   * A unique ID for the modal target\n   */\n  id?: string\n}\n```\n\n#### Example\n\n```jsx harmony\nconst Component = () =\u003e {\n  const {open, close, toggle, isOpen} = useModal()\n  return \u003cbutton onClick={toggle}\u003eToggle the modal\u003c/button\u003e\n}\n```\n\n## LICENSE\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faccessible-ui%2Fmodal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faccessible-ui%2Fmodal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faccessible-ui%2Fmodal/lists"}