Mobile keyboard covering your input or fixed footer? How to fix it on iPhone and Android

Why the phone keyboard hides the input or the Send button, why iPhone and Android get it wrong in different ways, and the fix for each.

Updated 5 min read

FormsGalaxy A06 · 360 × 800
Galaxy A06 running a web app, with the viewport dimension lines

Short answer: when the keyboard opens, iPhone and Android change different things. On iPhone the page keeps its size and only the visible area shrinks, so a position: fixed footer ends up behind the keyboard. On Android with interactive-widget=resizes-content, the page really shrinks and the fixed footer rides up with the keyboard, where it can land on top of the input. The fix is to pick a behavior in the viewport meta tag and track the keyboard height with visualViewport wherever that setting isn’t honored.

Two viewports

A mobile browser measures the screen twice:

  • Layout viewport: the size of the page. position: fixed, 100vh, 100dvh, innerHeight and height media queries all follow it.
  • Visual viewport: the part the user can see right now. It shrinks with pinch zoom and with the keyboard. JavaScript reads it from window.visualViewport.

The keyboard always shrinks the visual viewport. The question is whether it shrinks the layout viewport too, and each platform answers differently.

iOS never resizes the layout viewport for the keyboard, in any browser (they all run on WebKit). innerHeight and 100dvh stay the same; only visualViewport.height goes down. So:

  • A footer or tab bar with bottom: 0 sits behind the keyboard.
  • A Send button that lives in a fixed footer disappears while the user types.
  • iOS scrolls the page so the focused field shows, but only if the page can scroll. In app-like layouts with body { overflow: hidden } and a fixed height, it pushes the whole screen up instead, fixed header included.

iOS ignores interactive-widget. Changing the viewport meta tag does nothing there.

On Android: it depends on interactive-widget

Since Chrome 108, Android defaults to the same model as iPhone: the keyboard only shrinks the visual viewport. The viewport meta tag changes that:

<meta name="viewport" content="width=device-width, initial-scale=1, interactive-widget=resizes-content">
Value What the keyboard does
resizes-visual (default) Shrinks only the visible area, like iPhone. The fixed footer stays behind the keyboard.
resizes-content Shrinks the page. innerHeight, 100dvh and height media queries change, and the fixed footer moves up to the top of the keyboard.
overlays-content Changes nothing. The keyboard just covers the page.

resizes-content is what most chat UIs want: the message box sticks to the keyboard. It also creates the classic bug. If the thing fixed at the bottom is the tab bar rather than the message box, the tab bar rides up with the keyboard and sits on top of the input and the Send button. It looks fine at rest. The moment someone starts typing, they can’t see what they write.

The fix

1. Decide what stays pinned to the bottom

With the keyboard open, only what the user needs for typing should stay attached to it. Tab bars, banners and floating buttons get out of the way:

@media (pointer: coarse) {
  body:has(input:focus, textarea:focus, [contenteditable]:focus) .tabs {
    display: none;
  }
}

This fixes Android with resizes-content and doesn’t hurt iPhone, where the tab bar would be behind the keyboard anyway. Don’t hide it by screen height (max-height: 500px): a phone in landscape with no keyboard matches that too.

2. Track the keyboard with visualViewport

Where the layout viewport doesn’t shrink (iPhone, and Android by default), read the keyboard height and lift what has to stay visible:

const vv = window.visualViewport;

function keyboard() {
  const height = Math.max(0, window.innerHeight - vv.height - vv.offsetTop);
  document.documentElement.style.setProperty('--keyboard', `${height}px`);
}

vv.addEventListener('resize', keyboard);
vv.addEventListener('scroll', keyboard);
keyboard();
.message-box {
  position: fixed;
  left: 0;
  right: 0;
  bottom: var(--keyboard, 0px);
}

On Android with resizes-content, innerHeight has already shrunk, the math comes out to zero, and the same code works on both platforms.

3. Give the page room to scroll

On a regular form, don’t lock body to 100vh with overflow: hidden. If the page can scroll, the browser brings the focused field above the keyboard on its own. If the field lives inside its own scroll container, call field.scrollIntoView({ block: 'center' }) on focus.

What doesn’t fix it

  • Switching position: fixed to absolute. The footer stops covering things, and also stops staying at the bottom of the screen.
  • env(keyboard-inset-height) and the VirtualKeyboard API. Chromium only, and only after navigator.virtualKeyboard.overlaysContent = true. iPhone doesn’t have the API.
  • 100dvh on its own. On iPhone it doesn’t change with the keyboard. On Android, only with resizes-content.

How to test it

Chrome’s device mode never opens a keyboard, which is why this bug ships so often: on a laptop you type on the physical keyboard and the screen never changes.

In Breakpane, tap a field on a phone and the keyboard slides up on the device screen with that platform’s rules: on iPhone the page keeps its size and only the visible area shrinks; on Android it follows the interactive-widget in your viewport meta tag. Open an iPhone and a Galaxy side by side and you see right away whether the Send button went behind the keyboard or the tab bar climbed on top of the input. Toggle it with ⇧⌘K (Ctrl+Shift+K on Windows).

Two limits: the iPhone case where the whole screen gets pushed up because the page can’t scroll isn’t reproduced, and the VirtualKeyboard API reports zero. If your layout depends on either, check once on a phone. For what the browser bar does while scrolling, see 100vh in iPhone Safari.

Checklist

  • Viewport meta tag with an interactive-widget value chosen on purpose.
  • With the keyboard open, tab bars and floating buttons get out of the way.
  • Input and Send button visible with the keyboard open, on an iPhone and an Android phone, portrait and landscape.
  • The page can scroll, or the field calls scrollIntoView on focus.

Other guides

  1. FormsiPhone 16 · 393 × 852
    iPhone 16 running a web app, with the 393 point width dimension line
    How to stop iPhone Safari zooming in on input focus (without breaking pinch zoom)
  2. SimulatorsPixel 9 · 412 × 923
    Pixel 9 showing a banking web app statement, with the viewport height dimension line
    Mobile simulators for testing a website: every option on PC and Mac
  3. Mobile viewGalaxy S25 · 360 × 780
    Galaxy S25 running a web app, with the 360 point width dimension line
    How to view the mobile version of a website on desktop