Skip to content
Back to Blog
Mobile Development

Improving Rendering Efficiency in React Native Interfaces

5 min read
By Skynor Labs Team
Improving Rendering Efficiency in React Native Interfaces

A slow screen can involve expensive React updates, repeated layout, image decoding or native drawing. Reducing the number of renders helps only when those renders contribute meaningful work. A render count alone does not show which change will improve the interaction.

Choose one user action to investigate: selecting a row, typing into a search field, opening a sheet or scrolling a populated feed. Keep that flow unchanged while comparing implementations.

Find the expensive update

Use the profiler in React Native DevTools to inspect component renders and React commits. Record what happens during the action, then check whether expensive work comes from a row, its parent, a context update or unrelated screen state.

Where the installed React Native version supports performance tracing, correlate JavaScript work with the interaction. If React work is short but the screen still stalls, investigate native layout and drawing instead of adding memoization to every component.

A development profile identifies suspects; it is not a release benchmark. Check the final change on a physical device with a release build and the same data.

Keep state near the interaction

An input's draft text does not usually need to live in the same state owner as every row in a feed. Keep transient state close to the components that use it. Split a frequently changing context if it forces unrelated consumers to update.

Preserve item identity when updating a collection. Creating new objects for every row can make unchanged items appear different to a memoized component. Mutating existing objects has the opposite problem: a shallow comparison may miss a real change.

Make list selection explicit

This example uses a stable item ID, a memoized row and extraData for selection. It assumes the parent supplies immutable records and an onSelect callback whose identity is stable when its behavior has not changed.

import { memo, useCallback } from 'react';
import {
  FlatList,
  Pressable,
  StyleSheet,
  Text,
  type ListRenderItemInfo,
} from 'react-native';

type Item = { id: string; title: string };
type RowProps = {
  item: Item;
  selected: boolean;
  onSelect: (id: string) => void;
};

const Row = memo(function Row({ item, selected, onSelect }: RowProps) {
  return (
    <Pressable
      accessibilityRole="button"
      accessibilityState={{ selected }}
      onPress={() => onSelect(item.id)}
      style={[styles.row, selected && styles.selected]}
    >
      <Text>{item.title}</Text>
    </Pressable>
  );
});

type ListProps = {
  items: Item[];
  selectedId: string | null;
  onSelect: (id: string) => void;
};

export function ItemList({ items, selectedId, onSelect }: ListProps) {
  const renderItem = useCallback(
    ({ item }: ListRenderItemInfo<Item>) => (
      <Row item={item} selected={item.id === selectedId} onSelect={onSelect} />
    ),
    [selectedId, onSelect],
  );

  return (
    <FlatList
      data={items}
      keyExtractor={(item) => item.id}
      renderItem={renderItem}
      extraData={selectedId}
    />
  );
}

const styles = StyleSheet.create({
  row: { minHeight: 56, padding: 16 },
  selected: { backgroundColor: '#d8e9ef' },
});

extraData makes the external selection dependency explicit to the list. The old and new selected rows need to change; other rows can skip rendering when their props remain equal. The inline press handler is inside the row, so it is not a new callback prop passed from the parent on every parent render.

This is a pattern to evaluate in the application's profiler, not evidence of a measured speedup. The FlatList reference documents its shallow-comparison behavior and the role of extraData.

Treat memoization as a targeted optimization

React's memo reference explains that memoization can skip renders when props are unchanged. It does not prevent updates from a component's own state or a context it consumes. Newly created object and function props can also defeat that comparison.

Use useMemo for a measured expensive calculation or a value whose stable identity helps a consumer. Use useCallback when callback identity matters to that consumer. Include all reactive dependencies rather than freezing a stale value to obtain fewer renders.

If React Compiler is enabled for the project, check which components it already optimizes before adding manual wrappers. Keep profiling and correctness checks in place; memoization should not be required to make selection, subscriptions or data updates work correctly.

Tune lists against their actual content

Use getItemLayout only when item sizes are known, accounting for separators. Rows with changing text size, expanded content or variable images need a different strategy; an incorrect offset can cause scrolling errors.

Choose the initial render count from the visible screen area. Adjust the render window and batch size while checking fast scrolling, blank areas, touch responsiveness and memory. A larger batch may fill the viewport sooner while delaying an unrelated touch.

The FlatList configuration guide describes these tradeoffs. Test long titles, accessibility text sizes and device orientation before treating a fixed configuration as complete.

Verify behavior alongside frame consistency

Repeat the original action with realistic data, rapid repeated input and slower devices. Check that selection updates immediately, no records disappear, focus remains usable and list positions are preserved.

Compare profiles for the same build conditions, then confirm the user-visible result in release. Record the target devices and the flow that improved. Avoid turning a result on one screen into a promise of identical performance across the app.

For an existing slow screen, our React Native performance audit starts with its update path and device behavior. Describe the interaction so we can assess the work.

Discuss your engineering priorities

Tell us what your product does, what is getting in the way and what you need to ship next. We can review the scope together.

Discuss a project