CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-native-keyboard-aware-scroll-view

A React Native ScrollView component that resizes when the keyboard appears.

Pending
Overview
Eval results
Files

sectionlist-component.mddocs/

KeyboardAwareSectionList Component

The KeyboardAwareSectionList is a SectionList component that automatically handles keyboard appearance by scrolling to keep focused TextInput fields visible. It extends React Native's SectionList with keyboard awareness capabilities, making it ideal for sectioned lists containing input fields.

Capabilities

Component Class

A SectionList component enhanced with keyboard awareness functionality.

/**
 * A SectionList component that automatically scrolls to keep focused TextInput fields visible
 * when the keyboard appears. Extends SectionList with keyboard-aware behavior.
 */
export class KeyboardAwareSectionList<ItemT = any> extends React.Component<
  KeyboardAwareSectionListProps<ItemT>,
  KeyboardAwareState
> {
  getScrollResponder(): any;
  scrollToPosition(x: number, y: number, animated?: boolean): void;
  scrollToEnd(animated?: boolean): void;
  scrollForExtraHeightOnAndroid(extraHeight: number): void;
  scrollToFocusedInput(
    reactNode: any,
    extraHeight?: number,
    keyboardOpeningTime?: number
  ): void;
  scrollIntoView(
    element: React.ReactElement,
    options?: ScrollIntoViewOptions
  ): Promise<void>;
  update(): void;
}

interface KeyboardAwareSectionListProps<ItemT> extends KeyboardAwareProps, SectionListProps<ItemT> {}

Usage Examples:

import React, { useState } from 'react';
import { View, TextInput, Text, StyleSheet } from 'react-native';
import { KeyboardAwareSectionList } from 'react-native-keyboard-aware-scroll-view';

interface ContactSection {
  title: string;
  data: Contact[];
}

interface Contact {
  id: string;
  name: string;
  email: string;
  phone: string;
}

// Basic usage with sectioned contact form
export function ContactForm() {
  const [sections] = useState<ContactSection[]>([
    {
      title: 'Personal Contacts',
      data: [
        { id: '1', name: '', email: '', phone: '' },
        { id: '2', name: '', email: '', phone: '' },
      ]
    },
    {
      title: 'Work Contacts',
      data: [
        { id: '3', name: '', email: '', phone: '' },
        { id: '4', name: '', email: '', phone: '' },
      ]
    },
    {
      title: 'Emergency Contacts',
      data: [
        { id: '5', name: '', email: '', phone: '' },
      ]
    }
  ]);

  const renderContactItem = ({ item }: { item: Contact }) => (
    <View style={styles.contactItem}>
      <TextInput
        style={styles.input}
        placeholder="Name"
        defaultValue={item.name}
      />
      <TextInput
        style={styles.input}
        placeholder="Email"
        defaultValue={item.email}
        keyboardType="email-address"
      />
      <TextInput
        style={styles.input}
        placeholder="Phone"
        defaultValue={item.phone}
        keyboardType="phone-pad"
      />
    </View>
  );

  const renderSectionHeader = ({ section }: { section: ContactSection }) => (
    <View style={styles.sectionHeader}>
      <Text style={styles.sectionTitle}>{section.title}</Text>
    </View>
  );

  return (
    <KeyboardAwareSectionList
      sections={sections}
      renderItem={renderContactItem}
      renderSectionHeader={renderSectionHeader}
      keyExtractor={item => item.id}
      style={styles.container}
      enableOnAndroid={true}
      extraHeight={75}
    />
  );
}

// Advanced usage with mixed content types
export function SettingsForm() {
  const [settingsSections] = useState([
    {
      title: 'Account Settings',
      data: [
        { id: 'username', type: 'input', label: 'Username', value: '' },
        { id: 'email', type: 'input', label: 'Email', value: '' },
        { id: 'bio', type: 'textarea', label: 'Bio', value: '' },
      ]
    },
    {
      title: 'Preferences',
      data: [
        { id: 'theme', type: 'input', label: 'Theme Color', value: '' },
        { id: 'language', type: 'input', label: 'Language', value: '' },
      ]
    }
  ]);

  const renderSettingItem = ({ item }: { item: any }) => (
    <View style={styles.settingItem}>
      <Text style={styles.settingLabel}>{item.label}</Text>
      {item.type === 'textarea' ? (
        <TextInput
          style={[styles.input, styles.textarea]}
          placeholder={`Enter ${item.label.toLowerCase()}`}
          defaultValue={item.value}
          multiline
          numberOfLines={3}
        />
      ) : (
        <TextInput
          style={styles.input}
          placeholder={`Enter ${item.label.toLowerCase()}`}
          defaultValue={item.value}
        />
      )}
    </View>
  );

  const renderSectionHeader = ({ section }: { section: any }) => (
    <View style={styles.sectionHeader}>
      <Text style={styles.sectionTitle}>{section.title}</Text>
    </View>
  );

  return (
    <KeyboardAwareSectionList
      sections={settingsSections}
      renderItem={renderSettingItem}
      renderSectionHeader={renderSectionHeader}
      keyExtractor={item => item.id}
      style={styles.container}
      enableOnAndroid={true}
      enableAutomaticScroll={true}
      extraHeight={100}
      extraScrollHeight={50}
      keyboardOpeningTime={250}
      resetScrollToCoords={{ x: 0, y: 0 }}
      enableResetScrollToCoords={true}
      stickySectionHeadersEnabled={true}
      onKeyboardWillShow={(frames) => console.log('Keyboard will show', frames)}
    />
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#f5f5f5' },
  sectionHeader: {
    backgroundColor: '#e0e0e0',
    padding: 15,
    borderBottomWidth: 1,
    borderBottomColor: '#ccc',
  },
  sectionTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#333',
  },
  contactItem: {
    backgroundColor: 'white',
    padding: 15,
    borderBottomWidth: 1,
    borderBottomColor: '#eee',
  },
  settingItem: {
    backgroundColor: 'white',
    padding: 15,
    borderBottomWidth: 1,
    borderBottomColor: '#eee',
  },
  settingLabel: {
    fontSize: 14,
    fontWeight: '500',
    marginBottom: 8,
    color: '#333',
  },
  input: {
    borderWidth: 1,
    borderColor: '#ccc',
    padding: 10,
    marginBottom: 10,
    borderRadius: 5,
    backgroundColor: '#fafafa',
  },
  textarea: {
    height: 80,
    textAlignVertical: 'top',
  },
});

Get Scroll Responder

Gets the underlying SectionList's scroll responder for advanced scroll operations.

/**
 * Get the underlying SectionList's scroll responder
 * @returns The scroll responder instance
 */
getScrollResponder(): any;

Scroll to Position

Programmatically scrolls to a specific position in the SectionList.

/**
 * Scroll to specific position with or without animation
 * @param x - The x coordinate to scroll to
 * @param y - The y coordinate to scroll to
 * @param animated - Whether to animate the scroll (default: true)
 */
scrollToPosition(x: number, y: number, animated?: boolean): void;

Scroll to End

Scrolls to the end of the SectionList content.

/**
 * Scroll to end with or without animation
 * @param animated - Whether to animate the scroll (default: true)
 */
scrollToEnd(animated?: boolean): void;

Usage Example:

import React, { useRef } from 'react';
import { Button } from 'react-native';
import { KeyboardAwareSectionList } from 'react-native-keyboard-aware-scroll-view';

export function ScrollableSectionList({ sections }: { sections: any[] }) {
  const sectionListRef = useRef<KeyboardAwareSectionList>(null);

  const scrollToTop = () => {
    sectionListRef.current?.scrollToPosition(0, 0, true);
  };

  const scrollToEnd = () => {
    sectionListRef.current?.scrollToEnd(true);
  };

  return (
    <>
      <KeyboardAwareSectionList
        ref={sectionListRef}
        sections={sections}
        renderItem={({ item }) => <YourItemComponent item={item} />}
        renderSectionHeader={({ section }) => <YourHeaderComponent section={section} />}
        keyExtractor={item => item.id}
      />
      <Button title="Scroll to Top" onPress={scrollToTop} />
      <Button title="Scroll to End" onPress={scrollToEnd} />
    </>
  );
}

Android Extra Height Scroll

Android-specific method for scrolling with additional height offset.

/**
 * Android-specific scroll method with extra height offset (Android only)
 * @param extraHeight - Additional height to add to the scroll offset
 */
scrollForExtraHeightOnAndroid(extraHeight: number): void;

Scroll to Focused Input

Scrolls to a specific focused TextInput field within a section item.

/**
 * Scroll to a specific focused input field
 * @param reactNode - The React node handle of the input to scroll to
 * @param extraHeight - Additional height offset (optional)
 * @param keyboardOpeningTime - Custom keyboard opening delay (optional)
 */
scrollToFocusedInput(
  reactNode: any,
  extraHeight?: number,
  keyboardOpeningTime?: number
): void;

Scroll Into View

Scrolls a React element into view with customizable positioning.

/**
 * Scrolls an element into view with customizable positioning
 * @param element - The React element to scroll into view
 * @param options - Configuration options for scroll positioning
 * @returns Promise that resolves when scrolling is complete
 */
scrollIntoView(
  element: React.ReactElement,
  options?: ScrollIntoViewOptions
): Promise<void>;

Update

Manually triggers scrolling to the currently focused input field.

/**
 * Manually trigger scroll to currently focused input
 * Useful for updating scroll position after layout changes
 */
update(): void;

Props

The KeyboardAwareSectionList accepts all standard SectionList props plus the KeyboardAwareProps interface:

interface KeyboardAwareSectionListProps<ItemT> extends KeyboardAwareProps, SectionListProps<ItemT> {
  // Inherits all SectionList props (sections, renderItem, renderSectionHeader, etc.)
  // Plus all KeyboardAwareProps (see main documentation)
}

Common Use Cases

User Profile Sections

import React, { useState } from 'react';
import { View, TextInput, Text } from 'react-native';
import { KeyboardAwareSectionList } from 'react-native-keyboard-aware-scroll-view';

export function UserProfileSections() {
  const [profileSections] = useState([
    {
      title: 'Basic Information',
      data: [
        { id: 'firstName', label: 'First Name', value: '' },
        { id: 'lastName', label: 'Last Name', value: '' },
        { id: 'email', label: 'Email', value: '' },
      ]
    },
    {
      title: 'Contact Details',
      data: [
        { id: 'phone', label: 'Phone Number', value: '' },
        { id: 'address', label: 'Address', value: '' },
        { id: 'city', label: 'City', value: '' },
      ]
    },
    {
      title: 'Additional Info',
      data: [
        { id: 'bio', label: 'Bio', value: '', multiline: true },
        { id: 'website', label: 'Website', value: '' },
      ]
    }
  ]);

  const renderProfileItem = ({ item }: { item: any }) => (
    <View style={{ padding: 15, backgroundColor: 'white' }}>
      <Text style={{ marginBottom: 8, fontWeight: '500' }}>{item.label}</Text>
      <TextInput
        style={{
          borderWidth: 1,
          borderColor: '#ccc',
          padding: 10,
          borderRadius: 5,
          height: item.multiline ? 80 : 40,
        }}
        placeholder={`Enter ${item.label.toLowerCase()}`}
        defaultValue={item.value}
        multiline={item.multiline}
        textAlignVertical={item.multiline ? 'top' : 'center'}
      />
    </View>
  );

  const renderSectionHeader = ({ section }: { section: any }) => (
    <View style={{ backgroundColor: '#f0f0f0', padding: 12 }}>
      <Text style={{ fontSize: 16, fontWeight: 'bold' }}>{section.title}</Text>
    </View>
  );

  return (
    <KeyboardAwareSectionList
      sections={profileSections}
      renderItem={renderProfileItem}
      renderSectionHeader={renderSectionHeader}
      keyExtractor={item => item.id}
      enableOnAndroid={true}
      extraHeight={100}
      stickySectionHeadersEnabled={true}
    />
  );
}

Platform Support

  • iOS: Full support for all features
  • Android: Requires enableOnAndroid={true} and windowSoftInputMode="adjustPan" in AndroidManifest.xml for full functionality

Install with Tessl CLI

npx tessl i tessl/npm-react-native-keyboard-aware-scroll-view

docs

flatlist-component.md

hoc-component.md

index.md

scrollview-component.md

sectionlist-component.md

tile.json