diff --git a/react-responsive-modal/src/index.tsx b/react-responsive-modal/src/index.tsx index 2427009d..a914c362 100644 --- a/react-responsive-modal/src/index.tsx +++ b/react-responsive-modal/src/index.tsx @@ -158,6 +158,11 @@ export interface ModalProps { * Callback fired when the Modal has exited and the animation is finished. */ onAnimationEnd?: () => void; + /** + * Keeps the Modal mounted even when it is hidden. This is useful for keeping the DOM state + * inside the Modal as well as for SEO purposes. + */ + keepMounted?: boolean; children?: React.ReactNode; } @@ -187,6 +192,7 @@ export const Modal = React.forwardRef( onEscKeyDown, onOverlayClick, onAnimationEnd, + keepMounted = false, children, reserveScrollBarGap, }: ModalProps, @@ -206,6 +212,9 @@ export const Modal = React.forwardRef( // it will match the server rendered content const [showPortal, setShowPortal] = useState(false); + // Used to hide the modal when keepMounted is true + const [display, setDisplay] = useState(false); + // Hook used to manage multiple modals opened at the same time useModalManager(refModal, open); @@ -260,10 +269,13 @@ export const Modal = React.forwardRef( useEffect(() => { // If the open prop is changing, we need to open the modal // This is also called on the first render if the open prop is true when the modal is created - if (open && !showPortal) { + if ((open || keepMounted) && !showPortal) { setShowPortal(true); handleOpen(); } + if (open && !display) { + setDisplay(true); + } }, [open]); const handleClickOverlay = ( @@ -293,7 +305,10 @@ export const Modal = React.forwardRef( const handleAnimationEnd = () => { if (!open) { - setShowPortal(false); + setDisplay(false); + if (!keepMounted) { + setShowPortal(false); + } } onAnimationEnd?.(); @@ -313,7 +328,10 @@ export const Modal = React.forwardRef( ? ReactDom.createPortal(
JSX.Element> = { longContent: LongContent, focusTrapped: FocusTrapped, focusTrappedInitialFocus: FocusTrappedInitialFocus, + keepMounted: KeepMounted, customCssStyle: CustomCssStyle, customAnimation: CustomAnimation, customCloseIcon: CustomCloseIcon, diff --git a/website/src/docs/index.mdx b/website/src/docs/index.mdx index fe9467ec..bc960168 100644 --- a/website/src/docs/index.mdx +++ b/website/src/docs/index.mdx @@ -8,6 +8,7 @@ A simple responsive and accessible react modal. - Multiple modals. - Accessible modals. - Easily customizable via props. +- Optionally keep hidden modals mounted to the DOM - Typescript support - [Small bundle size](https://bundlephobia.com/result?p=react-responsive-modal) @@ -103,6 +104,26 @@ You can also set to trap focus within the modal, but decide where to put focus w ``` +### Keeping a hidden modal mounted + +By setting the `keepMounted` property to `true`, you can specify that the modal should always be mounted to the DOM, even when hidden it is hidden. +Instead of removing the DOM nodes, the modal is hidden using `display: none`. + +```js + + ... + +``` + +This is useful when you want the state of the DOM nodes (e.g. the text inside an `` element) to stay alive, as well as for search engine optimization (Google can see the content of the hidden modal). + +Press the button below and watch the element tree in your browser. The modal is a `
` at the very bottom of the ``, and will not disappear even when the modal is closed! + + + ### Custom styling with css Customising the Modal style via css is really easy. For example if you add the following css to your app you will get the following result: @@ -205,6 +226,7 @@ By default, the Modal will be rendered at the end of the html body tag. If you w | **onEscKeyDown\*** | `(event: KeyboardEvent) => void` | | Callback fired when the escape key is pressed. | | **onOverlayClick\*** | `(event: React.MouseEvent) => void` | | Callback fired when the overlay is clicked. | | **onAnimationEnd\*** | `() => void` | | Callback fired when the Modal has exited and the animation is finished. | +| **keepMounted** | `boolean` | false | Keeps the Modal mounted to the DOM tree even when it is hidden. This is useful for keeping DOM state inside the Modal as well as for SEO purposes. | ## License diff --git a/website/src/examples/KeepMounted.tsx b/website/src/examples/KeepMounted.tsx new file mode 100644 index 00000000..d5e0fcd4 --- /dev/null +++ b/website/src/examples/KeepMounted.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Modal } from 'react-responsive-modal'; + +const App = () => { + const [open, setOpen] = React.useState(false); + + return ( + <> + + + setOpen(false)} keepMounted center> +
+ This modal will stay mounted even when closed. +
+
+ +
+
+ + ); +}; + +export default App; diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx index e8e4cf41..760bc1de 100644 --- a/website/src/pages/index.tsx +++ b/website/src/pages/index.tsx @@ -46,6 +46,14 @@ const IndexPage = () => ( Focus Trapped modal +
  • + + Keeping a hidden modal mounted + +