nextui
nextui copied to clipboard
[BUG] - Modal does not adjust position when keyboard appears on iOS
NextUI Version
2.3.5
Describe the bug
After updating from version 2.2.X to 2.3.X, there is an issue observed on iOS devices where the modal dialog does not adjust its position when the keyboard appears on the screen. As a result, the modal remains in its original position, obscuring the view of the input fields and preventing users from seeing what they are typing. This behavior affects the usability of the application, especially in forms and dialogs requiring user input.
Your Example Website or App
No response
Steps to Reproduce the Bug or Issue
- Create a webpage that includes a modal dialog.
- Within the modal, include a form with several input fields.
- Open the webpage on an iOS device using Safari.
- Tap on the input field within the modal to bring up the iOS keyboard.
Notice that when the keyboard appears, the modal remains in its fixed position at the bottom of the screen. The keyboard covers the modal, obscuring the input field and preventing the user from seeing what is being typed.
Expected behavior
The modal should adjust its position when the keyboard appears, moving up to ensure that the input field is visible and the user can see what they are typing.
Screenshots or Videos
No response
Operating System Version
iOS 17.4.1
Browser
Safari
@jalvarezz13 can you attach a screenshot and some code? Also can you share how it looked in 2.2.x?
The code could be this modal https://nextui.org/docs/components/modal#with-form without "top-center" placement. I attach a simple demo here:
https://github.com/nextui-org/nextui/assets/64201633/49faa35e-df48-4239-8329-9544d008db1d
Facing the same issue after bumping to 2.3.5. Seems like a regression, everything worked just fine before update.
Im facing the same issue
Can also reproduce
Looks like it's somehow related to the internal implementation of the scroll-blocking behavior within Modal shouldBlockScroll
. Unfortunately, I'm short on time to dig into it more, what I ended up doing was disabling shouldBlockScroll
completely, and relying on custom implementation, using body-scroll-lock, which is fine for my case, since it's already used in the project. Tho I had to rely on useEffect
to make it work, which is unfortunate and I had to mimic <ModalBody />
classnames explicitly as I needed to get a scrollable modal content ref.
const ModalTemplate = <ContentWrapperProps extends TContentWrapperProps, ModalFooterProps extends TModalFooterProps>({ title, children, ModalContentWrapper = Fragment, modalContentWrapperProps, ModalFooter, getModalFooterProps, isLoading, ...modalProps }: IModalTemplateProps<ContentWrapperProps, ModalFooterProps>): ReactElement => {
const scrollableTargetRef = useRef<HTMLDivElement>(null);
useEffect(
(): void => {
if (modalProps.isOpen && scrollableTargetRef.current) {
disableBodyScroll(scrollableTargetRef.current);
return;
}
clearAllBodyScrollLocks();
},
[modalProps.isOpen],
);
return (
<Modal
size="2xl"
backdrop="blur"
scrollBehavior="inside"
hideCloseButton
shouldBlockScroll={false}
classNames={{
backdrop: 'top-0 left-0',
}}
{...modalProps}
>
<ModalContent>
{(close: () => void): ReactElement => (
<ModalContentWrapper
{...modalContentWrapperProps as ContentWrapperProps}
className="[display:contents]"
>
<ModalHeader className="flex flex-col gap-1">
<div className="flex w-full items-center justify-between gap-2">
{title}
<Button
variant="light"
isIconOnly
onPress={close}
>
<CloseIcon />
</Button>
</div>
</ModalHeader>
/** Explicit <ModalBody /> implementation. class names list was taken from nextui.Modal.ModalBody as is **/
<div
className="flex flex-1 flex-col gap-3 overflow-y-auto px-6 py-2"
ref={scrollableTargetRef}
>
{children}
</div>
{ModalFooter && (
<ModalFooterBase>
<ModalFooter
{...(typeof getModalFooterProps === 'function' && getModalFooterProps({
close,
isLoading,
})) as ModalFooterProps}
/>
</ModalFooterBase>
)}
</ModalContentWrapper>
)}
</ModalContent>
</Modal>
);
};
export default ModalTemplate;
same issue with 2.3.6
2.2.10 is working fine
Any update? Thanks!
In case it helps... I'm temporarily using @nextui-org/modal in version 2.0.29 until this bug is fixed.
@jrgarciadev The error persists in version 2.4.0
same issue is here https://github.com/nextui-org/nextui/issues/3241
#3241 anybody know how to fix this issue?
i'm having the same problem, does anyone have an idea on how to fix it?
i'm having the same problem, does anyone have an idea on how to fix it?
Just use the version of the modal from further up where it works. Install just the individual modal package version above and import the modal from that package instead of the central package
Same issue with version 2.0.36 of modal.
@kamami If you want to fix this issue, you can install the individual modal box using version 2.0.0. It is a little bit buggy too, but the keyboard issue does not occur in this version. However, in other versions, the keyboard hides the inputs in all versions.
https://www.npmjs.com/package/@nextui-org/modal
@theonlyway thanks! I also just kept the latest version and on mobile screens i adjusted the absolute position as others suggested here.
same issue here, installing @nextui-org/[email protected] works for now
Oof. 2.0.0 fixes this, but scrolling is broken even with scrollBehavior outside if the modal is too big... ruh roh.
EDIT: For scrollBehavior: outside to work you also need placement set to top.... hmm
TEMP SOLUTION
'use client';
import React, { useEffect, useRef } from 'react';
import {
Button,
useDisclosure,
Modal,
ModalContent,
ModalBody,
} from '@nextui-org/react';
import CreateLotForm from '../../components/forms/CreateLotForm';
const AddLotModal = () => {
const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure();
const modalBodyRef = useRef(null);
const handleOpen = () => {
onOpen();
};
useEffect(() => {
if (modalBodyRef.current) {
const inputs = modalBodyRef.current.querySelectorAll(
'input, textarea, select',
);
inputs.forEach((input) => {
input.addEventListener('focus', () => {
setTimeout(() => {
input.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 300);
});
});
}
}, [isOpen]);
return (
<div>
<div className="flex flex-wrap gap-3">
<Button
variant="flat"
color="warning"
onClick={handleOpen}
className="capitalize"
>
Add New
</Button>
</div>
<Modal
isOpen={isOpen}
onOpenChange={onOpenChange}
isDismissable={false}
scrollBehavior="outside"
size="5xl"
>
<ModalContent>
{() => (
<>
{/* empty div for more space or height */}
<div className="pt-12 block sm:hidden" />
<ModalBody ref={modalBodyRef}>
<CreateLotForm
enableReinitialize
close={onClose}
onSubmit={() => {}}
/>
{/* empty div for more space or height */}
<div className="pb-44 block sm:hidden" />
</ModalBody>
</>
)}
</ModalContent>
</Modal>
</div>
);
};
export default AddLotModal;
any update? :(
+1
any fixes, updates?
It's been almost 4 months since the bug appeared, and no one from the NextUI organization seems to have addressed it in this thread.
For those who cannot do the same, I recommend installing the NextUI modal component in isolation from the rest. Also, remember to install the following dependencies to avoid potential issues:
- @nextui-org/modal: ^2.0.29
- @nextui-org/system: ^2.1.0
- @nextui-org/theme: ^2.0.0
You can install them with:
yarn add @nextui-org/modal@^2.0.29 @nextui-org/system@^2.1.0 @nextui-org/theme@^2.0.0
I found a solution with css, tested it and it seems to work reasonably, hope it helps too.
BaseModal.tsx
import { Modal } from "@nextui-org/modal";
type BaseModalProps = {} & React.ComponentProps<typeof Modal>;
export function BaseModal({ children, backdrop = "opaque", ...props }: BaseModalProps) {
return (
<Modal
backdrop={backdrop}
classNames={{
wrapper: "pb-[65%] landscape:pb-[50vh] landscape:xl:pb-0",
}}
isDismissable={false}
placement="top"
scrollBehavior="outside"
{...props}
>
{children}
</Modal>
);
}
tailwind.config.ts
Add to your theme extension
portrait: {
raw: "(orientation: portrait)",
},
landscape: {
raw: "(orientation: landscape)",
},
Usage example
<BaseModal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1">Title</ModalHeader>
<ModalBody>
<span>Your modal body</span>
</ModalBody>
<ModalFooter>
<Button color="danger" variant="light" onPress={onClose}>
Close
</Button>
<Button color="primary" type="submit">
Save
</Button>
</ModalFooter>
</>
)}
</ModalContent>
</BaseModal>
I had to use “top-center”
placement as a temporary solution since the modal does not behave consistently on mobile screens. We are really looking forward to fixing this issue.
@wingkwong I see you have self-assigned this issue, are you working on a specific branch you can share? Thanks
@cristyz Thank you for sharing the temporal solution, it's not working for my specific use case but it's a good solution for other cases :)
I have managed to avoid this by using an external library, which is not ideal, but will do for now.
https://www.npmjs.com/package/use-detect-keyboard-open
const isKeyboardOpen = useDetectKeyboardOpen();
const placement = isKeyboardOpen ? "top" : "auto"
return <MyCustomModal isOpen={isOpen} placement={placement} onClose={onClose}/>
Any update or release plz?