{"id":32563057,"url":"https://github.com/blevs/react-portal-modal","last_synced_at":"2026-02-26T19:02:45.470Z","repository":{"id":39140021,"uuid":"261063414","full_name":"Blevs/react-portal-modal","owner":"Blevs","description":null,"archived":false,"fork":false,"pushed_at":"2023-01-05T20:06:03.000Z","size":4751,"stargazers_count":0,"open_issues_count":20,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2023-03-03T18:39:13.504Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Blevs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-04T02:27:30.000Z","updated_at":"2020-05-04T02:27:49.000Z","dependencies_parsed_at":"2023-02-04T14:01:46.056Z","dependency_job_id":null,"html_url":"https://github.com/Blevs/react-portal-modal","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"purl":"pkg:github/Blevs/react-portal-modal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blevs%2Freact-portal-modal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blevs%2Freact-portal-modal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blevs%2Freact-portal-modal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blevs%2Freact-portal-modal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Blevs","download_url":"https://codeload.github.com/Blevs/react-portal-modal/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blevs%2Freact-portal-modal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281549787,"owners_count":26520515,"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","status":"online","status_checked_at":"2025-10-29T02:00:06.901Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2025-10-29T02:56:22.908Z","updated_at":"2025-10-29T02:56:42.131Z","avatar_url":"https://github.com/Blevs.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React Modal - with Portals!\n\n\u003cimg src=\"https://i.imgur.com/Q6oZSEW.gif\" /\u003e\n\nLet's make a modal, that works anywhere. Commonly, we want modals, toasts, or other elements that position themselves absolutely in the browser. But, we need to be careful where we render these elements or they will end up being positioned relative to the wrong element, or get caught by an `overflow: hidden`.\n\nInstead of worrying about that, React gives us a fantastic tool called [Portals](https://reactjs.org/docs/portals.html). This allows us to render our components anywhere in the DOM tree that we want!\n\n```js\nconst SomeComponent = () =\u003e {\n  return (\n  \u003cdiv style={{ position: 'relative', overflow: 'hidden', width: '20px', height: '20px' }}\u003e\n    Some other content\n    \u003cModal\u003e\n      This will still fill up the screen, despite its 'parent' having css properties that would normally cause problems. \n    \u003c/Modal\u003e\n  \u003c/div\u003e\n  )\n};\n```\n\nThis involves some amount of DOM manipulation, which is a good thing! We can't forget that React exists in our web browser, our vanilla DOM and JS knowledge is still valuable, and React cannot abstract *everything* away.\n\n## Goals\n\n* [ ] Create a style a Modal component (which can be surprisingly difficult)\n* [ ] Conditionally render the Modal based on some state (i.e. on button press)\n* [ ] Put the Modal in a container that ruins its positioning with `position: relative`, `overflow: hidden`, etc.\n* [ ] Escape the container in the DOM using a [Portal](https://reactjs.org/docs/portals.html) in the Modal component\n  * [ ] Hardcode a portal root in `index.html`\n  * [ ] Create a `div`, once per component, to use a portal target\n  * [ ] Write a `useEffect` that correctly adds the portal target to the portal root on mount, and removes it on unmount\n* [ ] Make clicking outside the Modal dismiss it (a huge pet peeve of mine)\n* [ ] Prevent the page body from scrolling while the portal is open, while preserving scroll position. If you don't know how to do this, google it. That's why I put it here.\n\n## Stretch Goals\n\n* [ ] Write a `usePortal` hook\n* [ ] Understand what this comment means in the React documentation\n  ```\n  // The portal element is inserted in the DOM tree after\n  // the Modal's children are mounted, meaning that children\n  // will be mounted on a detached DOM node. If a child\n  // component requires to be attached to the DOM tree\n  // immediately when mounted, for example to measure a\n  // DOM node, or uses 'autoFocus' in a descendant, add\n  // state to Modal and only render the children when Modal\n  // is inserted in the DOM tree.\n  ```\n* [ ] Use a dynamic portal root, instead of hardcoding one into `index.html`. You can do this many ways, including a smart `useEffect`, a context provider, etc.\n* [ ] Close the modal when 'Escape' is pressed, regardless of what the keyboard is focusing.\n* [ ] Create a Toast component, and a dispatch function to add new toasts.\n\n## Notes\n\n* This trickery is necessary because of [the stacking context](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context) and [relative positioning](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Positioning).\n* The React Tree is **NOT** the DOM Tree. For example, Events will still propagate 'normally' through the React tree even if the resulting elements end up differently in the rendered DOM tree.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblevs%2Freact-portal-modal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblevs%2Freact-portal-modal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblevs%2Freact-portal-modal/lists"}