Lists, Forms & Keyboard Handling

Render long lists efficiently, build validated forms, and avoid keyboard overlap on iOS and Android.

Lists and forms are most of what apps do. They are also where AI-generated code most often causes jank and keyboard bugs.

Core concepts

  1. FlatList and FlashList: Virtualised lists render only visible rows. Provide keyExtractor, keep row components light and memoised, and consider FlashList for very long or complex lists. — FlashList
  2. Pull to refresh and pagination: Use refreshing/onRefresh and onEndReached with a page cursor, guarding against duplicate requests. — FlatList
  3. Validated forms: Define a schema, show field-level errors, disable submit while pending and handle server errors. React Hook Form with Zod is a common pairing. — React Hook Form
  4. Keyboard behaviour: Use KeyboardAvoidingView or a keyboard-aware scroll view, dismiss on tap outside, and set returnKeyType and onSubmitEditing so the keyboard flows naturally. — KeyboardAvoidingView

Concept videos

YouTube channels and searches for these topics. On a large screen they open in the reading panel next to the roadmap.

Explanation

Row components must be cheap

Heavy rows make scrolling stutter. Memoise row components and avoid creating new functions or objects inline on every render when rows are numerous.

Test with a long list

Seed 1,000 items during development so performance issues show up early.

What Is It?

Lists and forms are the two core interaction patterns of data-driven apps.

Why Should I Care?

Good defaults here prevent the most common performance and usability complaints.

Real World Analogy

A virtualised list is a conveyor belt showing only the items in front of you instead of laying every item on the floor.

Visual Mental Model

text
data -> FlatList (visible window) -> memoised Row
form input -> schema validation -> submit state -> server error

Smallest Possible Example

tsx
import { FlatList } from 'react-native';

<FlatList
  data={items}
  keyExtractor={(item) => item.id}
  renderItem={({ item }) => <Row item={item} />}
  onEndReached={loadMore}
  onEndReachedThreshold={0.5}
/>;

Common Mistakes

❌ Using array index as key.

Reordering causes wrong rows to update; use a stable id.

❌ No submit-pending state.

Users double-tap and create duplicates.

Build This Mini Project

Build a list of 1,000 seeded items with pull-to-refresh and pagination, plus a create form that validates and shows server errors.

Prompts for Cursor, Claude Code & Codex

Paste these into Cursor, Claude Code, Codex, or any coding agent. Replace the bracketed parts with your app details, and keep the agent in plan mode first so you review the approach before it edits files.

Optimise a slow list

text
This list scrolls poorly: [paste component]. Identify causes (inline functions, heavy rows, missing keys), apply fixes, and explain each. Keep behaviour identical.

Validated form

text
Create a sign-up form with email and password using react-hook-form and zod: inline errors, disabled submit while pending, keyboard flow, and server error display. Use components/ui.

Rules, databases & blogs to use

Resources

Threads in the same domain as this chapter — go deeper on iHateReading without leaving the roadmap.

How to build an AI chat app with Vercel AI SDK and Firebase cover

How to build an AI chat app with Vercel AI SDK and Firebase

Chat UI, streaming and backend wiring for an AI feature.

Expo new version SDK 52 cover

Expo new version SDK 52

What a real Expo SDK upgrade changes for app builders.