RORK LABJP
DEADLINE — From August 31, 2026, Google Play requires target API level 36 (Android 16) or higher for both new apps and updates to existing ones. Twelve days leftEXTENSION — If you cannot make the date, the deadline extension form in Play Console buys you until November 1. The extension is not automatic, so the request itself has to land before August 31TARGET SDK — Even for the Expo and React Native apps Rork produces, the targetSdkVersion is yours to verify. A template pinned to an older SDK will not meet the requirement on its ownRORK MAX — Where the original Rork emits React Native and Expo, Rork Max generates native Swift, covering iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessageFUNDING — A $15M seed round led by Left Lane Capital was announced on April 9, alongside the acquisition of app builder Paperline to bring in engineering talentiOS — Developer beta 6 of iOS 27 arrived on August 17 with the autumn release drawing closer. If you ship native Swift output, the new OS timeline feeds straight into your release planDEADLINE — From August 31, 2026, Google Play requires target API level 36 (Android 16) or higher for both new apps and updates to existing ones. Twelve days leftEXTENSION — If you cannot make the date, the deadline extension form in Play Console buys you until November 1. The extension is not automatic, so the request itself has to land before August 31TARGET SDK — Even for the Expo and React Native apps Rork produces, the targetSdkVersion is yours to verify. A template pinned to an older SDK will not meet the requirement on its ownRORK MAX — Where the original Rork emits React Native and Expo, Rork Max generates native Swift, covering iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessageFUNDING — A $15M seed round led by Left Lane Capital was announced on April 9, alongside the acquisition of app builder Paperline to bring in engineering talentiOS — Developer beta 6 of iOS 27 arrived on August 17 with the autumn release drawing closer. If you ship native Swift output, the new OS timeline feeds straight into your release plan
Articles/Dev Tools
Dev Tools/2026-04-15Intermediate

Fix Keyboard Hiding Input Fields in Rork Apps: A Complete Troubleshooting Guide

Solve the common issue of the on-screen keyboard overlapping text input fields in Rork apps. Learn KeyboardAvoidingView, ScrollView combinations, the useKeyboard hook, and SwiftUI keyboard avoidance with working code examples.

Rork536keyboardKeyboardAvoidingViewReact Native227Expo172troubleshooting66forms2

Build a form screen, tap a text field, and suddenly the keyboard slides up — covering the very input the user is trying to fill out. If you've been building apps with Rork, you've almost certainly hit this. What makes it especially frustrating is that iOS and Android behave differently, so a fix that works on one platform quietly breaks the other.

This guide walks through the root cause, then gives you working code for each scenario you're likely to encounter.

Why This Happens

React Native (the framework underlying Rork) doesn't automatically scroll content upward when the software keyboard appears. Unlike a mobile browser, the framework won't naturally resize or shift the layout to keep the focused input visible — you have to wire that up yourself.

The behavior also differs between platforms. iOS expects the padding behavior prop, while Android relies on the native windowSoftInputMode setting, which you can control in app.json for Expo managed projects.

First Check: softwareKeyboardLayoutMode in app.json

{
  "expo": {
    "android": {
      "softwareKeyboardLayoutMode": "pan"
    }
  }
}

Setting this to "pan" makes the entire screen slide up on Android when the keyboard appears. "resize" shrinks the content area instead. For most form screens, "pan" feels more natural.

One important caveat: this setting is Android-only. iOS ignores it entirely, so you'll need a separate solution for Apple devices.

The Standard Fix: KeyboardAvoidingView

import {
  KeyboardAvoidingView,
  Platform,
  ScrollView,
  TextInput,
  StyleSheet,
} from 'react-native';
 
export default function ContactForm() {
  return (
    <KeyboardAvoidingView
      style={styles.container}
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      keyboardVerticalOffset={Platform.OS === 'ios' ? 90 : 0}
    >
      <ScrollView
        contentContainerStyle={styles.scrollContent}
        keyboardShouldPersistTaps="handled"
      >
        <TextInput
          style={styles.input}
          placeholder="Name"
          returnKeyType="next"
        />
        <TextInput
          style={styles.input}
          placeholder="Email address"
          keyboardType="email-address"
          returnKeyType="next"
        />
        <TextInput
          style={styles.input}
          placeholder="Message"
          multiline
          numberOfLines={6}
          textAlignVertical="top"
        />
      </ScrollView>
    </KeyboardAvoidingView>
  );
}
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  scrollContent: {
    padding: 16,
    paddingBottom: 40,
  },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    padding: 12,
    marginBottom: 16,
    fontSize: 16,
  },
});

Two things are critical here.

First, always branch the behavior prop with Platform.OS. iOS needs 'padding'; Android works better with 'height'. Leaving it undefined means iOS gets nothing.

Second, tune keyboardVerticalOffset to match your navigation bar height. With Expo Router's default stack header, 90 is usually close. If you're using a custom header, measure its actual height and adjust accordingly.

A Common Mistake: Forgetting the ScrollView

Adding KeyboardAvoidingView alone isn't enough if your form is tall. Without ScrollView inside it, inputs that scroll off the top of the screen are unreachable. Always pair them.

Also set keyboardShouldPersistTaps="handled" on the ScrollView. Without it, tapping outside a text field might not dismiss the keyboard, and taps on buttons inside the scroll area can get swallowed before they register.

For Chat-Style Layouts: useKeyboard Hook

When KeyboardAvoidingView doesn't behave (this happens frequently with bottom sheets, modals, and animated transitions), reading keyboard height directly gives you full control.

import { useEffect, useState } from 'react';
import { Keyboard, KeyboardEvent, Platform } from 'react-native';
 
export function useKeyboardHeight() {
  const [keyboardHeight, setKeyboardHeight] = useState(0);
 
  useEffect(() => {
    const showEvent =
      Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
    const hideEvent =
      Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
 
    const showSub = Keyboard.addListener(
      showEvent,
      (e: KeyboardEvent) => {
        setKeyboardHeight(e.endCoordinates.height);
      }
    );
    const hideSub = Keyboard.addListener(hideEvent, () => {
      setKeyboardHeight(0);
    });
 
    return () => {
      showSub.remove();
      hideSub.remove();
    };
  }, []);
 
  return keyboardHeight;
}
export default function ChatScreen() {
  const keyboardHeight = useKeyboardHeight();
 
  return (
    <View style={{ flex: 1 }}>
      <FlatList data={messages} /* ... */ />
      <View
        style={{
          paddingBottom: keyboardHeight > 0 ? keyboardHeight : 16,
          borderTopWidth: 1,
          borderTopColor: '#eee',
          padding: 8,
        }}
      >
        <TextInput placeholder="Type a message..." />
      </View>
    </View>
  );
}

This pattern works well for chat UIs where the input bar is pinned to the bottom. The re-render on keyboard height change is lightweight enough that it won't cause performance issues in a typical chat screen.

Rork Max (SwiftUI): Different Rules Apply

If you're generating a native SwiftUI app with Rork Max, the React Native approach doesn't apply. In SwiftUI, use .ignoresSafeArea(.keyboard) with a ScrollView.

struct ContactFormView: View {
    @State private var name = ""
    @State private var email = ""
    @State private var message = ""
 
    var body: some View {
        ScrollView {
            VStack(spacing: 16) {
                TextField("Name", text: $name)
                    .textFieldStyle(.roundedBorder)
                TextField("Email", text: $email)
                    .textFieldStyle(.roundedBorder)
                    .keyboardType(.emailAddress)
                TextEditor(text: $message)
                    .frame(height: 120)
                    .border(Color.gray.opacity(0.3))
            }
            .padding()
        }
        // Required when TextEditor is near the bottom of the screen
        .ignoresSafeArea(.keyboard, edges: .bottom)
    }
}

SwiftUI has built-in keyboard avoidance since iOS 15, but TextEditor specifically can still be obscured if the .ignoresSafeArea(.keyboard) modifier is missing. It's a small detail that's easy to overlook.

Tab Bar Screens Need Extra Offset

When your form is inside a tab-based navigation (Expo Router's Tabs, for example), add the tab bar height to keyboardVerticalOffset.

import { useSafeAreaInsets } from 'react-native-safe-area-context';
 
function FormInsideTabs() {
  const insets = useSafeAreaInsets();
  const TAB_BAR_HEIGHT = 49; // Expo Router default
 
  return (
    <KeyboardAvoidingView
      style={{ flex: 1 }}
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      keyboardVerticalOffset={TAB_BAR_HEIGHT + insets.bottom}
    >
      {/* form content */}
    </KeyboardAvoidingView>
  );
}

The keyboardVerticalOffset tells the component where to anchor its calculations. Leaving out the tab bar height causes the layout to shift by the wrong amount — sometimes barely noticeable, sometimes wildly off.

Choosing the Right Approach

  • Simple forms (login, contact): KeyboardAvoidingView + ScrollView covers most cases.
  • Chat UI or fixed bottom input: Use the useKeyboardHeight hook for reliable control.
  • Rork Max (SwiftUI): Add .ignoresSafeArea(.keyboard) and let SwiftUI handle the rest.
  • Screens inside a tab navigator: Include tab bar height in keyboardVerticalOffset.

For related layout issues, see the Rork App Layout and Responsive Design Troubleshooting Guide. If you want to add input validation alongside these fixes, Form Validation with react-hook-form and Zod walks through that step by step.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-04-20
Why useEffect Loops Infinitely in Rork-Generated Code — and How to Fix It
Rork-generated React Native code can trigger useEffect infinite loops through subtle dependency array mistakes. This guide covers 5 common causes — stale deps, object references, unstable callbacks — with Before/After code fixes.
Dev Tools2026-04-12
Rork App Running Slow, Lagging, or Freezing? A Complete Performance Troubleshooting Guide
Diagnose and fix performance issues in your Rork-built app — from laggy scrolling and slow transitions to freezes and memory leaks. Includes code examples for React Native optimization.
Dev Tools2026-07-30
What Renovate may bump in an Expo app, and what it must never touch
Turning on automated dependency updates in a Rork-generated app also hands Renovate the 123 packages Expo SDK 57 pins. Measured on 2026-07-30, six of them sit a full major version behind npm latest. Here is how to generate the ignore list from the SDK instead of maintaining it by hand.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →