Hey everyone,
I’m building a small bottom-sheet style modal in Ionic React that contains an <IonInput> and should open cleanly above the keyboard. On some devices it looks perfect, and on others it behaves completely differently especially when the keyboard is open.
What I’m trying to achieve
A modal that:
-
opens as a compact bottom sheet
-
has rounded corners
-
autofocuses the input
-
shifts up with the keyboard
-
uses a small breakpoint like
0.22so it stays tight and minimal
Basically a consistent “native iOS input sheet” style.
The issues
Across different devices (Android phones, iPhones, tablets), the modal behaves inconsistently:
1. The breakpoint height changes drastically between devices.
initialBreakpoint={0.22} looks perfect on smaller screens, but becomes very tall on larger screens, losing the “bottom sheet” look.
2. When the keyboard opens, the modal does not resize.
It leaves a large empty area above the keyboard instead of adjusting.
3. Even with:
Keyboard: {
resize: KeyboardResize.Body
}
…the modal still does not react to keyboard height changes.
My question
Is there a recommended or official way to make an Ionic modal behave like a consistent, keyboard-aware bottom sheet across ALL device sizes?
Or maybe a different component I should be using?
Or is this just a current limitation of the modal + breakpoint system?
Any guidance or examples are appreciated.
Here’s my current code (simplified):
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonInput,
IonModal,
IonToolbar,
} from "@ionic/react";
import React, { useRef } from "react";
import { listOutline, calendarNumberOutline } from "ionicons/icons";
import "../theme/RevisionedAddTask.css";
type Props = {
modal: React.RefObject<HTMLIonModalElement | null>;
setShowAddModal: (t: boolean) => void;
showAddModal: boolean;
};
const RevisionedAddTask: React.FC<Props> = ({
modal,
showAddModal,
setShowAddModal,
}) => {
const inputRef = useRef<HTMLIonInputElement>(null);
return (
<IonModal
ref={modal}
isOpen={showAddModal}
initialBreakpoint={0.22}
onWillPresent={() => console.log("Modal will present")}
onDidPresent={() => {
console.log("Modal did present – focusing input");
inputRef.current?.setFocus();
}}
onDidDismiss={({ detail }) => {
setShowAddModal(false);
console.log(`Dismissed with role: ${detail.role}`);
}}
showBackdrop={true}
handle={false}
animated={false}
className="keyboard-shift rounded-modal"
>
<IonContent
onMouseDown={(e) => e.preventDefault()}
onTouchStart={(e) => e.preventDefault()}
className="ion-padding no-scroll"
>
<IonInput
ref={inputRef}
aria-label="Custom input"
placeholder="Task label"
counter={true}
maxlength={30}
shape="round"
/>
<IonButtons>
<IonButton size="small" fill="clear" shape="round" className="transparent-button">
<IonIcon slot="icon-only" icon={listOutline} />
</IonButton>
<IonButton size="small" fill="clear" shape="round" className="transparent-button">
<IonIcon slot="icon-only" icon={calendarNumberOutline} />
</IonButton>
<IonButton fill="clear" className="bottom-right">
Save
</IonButton>
</IonButtons>
</IonContent>
</IonModal>
);
};
export default RevisionedAddTask;
Thanks!
