A header-presence conditional selects layout spacing. The only caller always supplies the header, including when its tab changes, so paddingTop stays at 0. The rule nevertheless asserts that changing padding makes rows jump.
Reproduction
Reproduced with React Doctor 0.9.13, Node 24.18.0, on macOS. JSON report: schemaVersion: 3, ok: true, project complete: true. This is a static-analysis reproduction; no runtime jank is inferred from the warning.
Create a directory with this package.json, add the file below, and run:
npx --yes react-doctor@0.9.13 . --scope full --json --blocking none --yes --no-score
{"name":"react-doctor-rule-repros","private":true,"version":"1.0.0","dependencies":{"react":"19.2.7","react-native":"0.86.2","@shopify/flash-list":"2.3.2","@tanstack/react-query":"5.101.0","zustand":"5.0.14"}}
HeaderGrid.tsx:
import type { ReactElement } from 'react';
import { FlashList } from '@shopify/flash-list';
import { Text } from 'react-native';
const rows = [{ id: 'one' }];
function renderRow({ item }: { item: { id: string } }) {
return <Text>{item.id}</Text>;
}
function Grid({ ListHeaderComponent }: { ListHeaderComponent?: ReactElement }) {
return (
<FlashList
data={rows}
keyExtractor={(item) => item.id}
renderItem={renderRow}
ListHeaderComponent={ListHeaderComponent}
contentContainerStyle={{ paddingTop: ListHeaderComponent ? 0 : 16 }}
/>
);
}
export function Screen({ selectedTab }: { selectedTab: 'profile' | 'badges' }) {
// The header is present for both tabs: paddingTop is always zero.
return <Grid ListHeaderComponent={<Text>{selectedTab}</Text>} />;
}
Actual
“Your users see rows jump when a changing paddingTop on contentContainerStyle shifts the whole list.”
The recommendation is contentInset={{ bottom: dynamicValue }}. That changes the edge from top to bottom and does not preserve this cross-platform content-spacing behavior.
Expected
Treat an unresolved expression as a potential cost, not proof that the value changes or causes jank. Either recognize statically provable stable cases or give conditional profiling guidance. The recommendation should preserve the affected edge and explain that contentInset is not a general cross-platform substitute for content padding.
Notes
This is not a request to exempt every prop-based expression: keyboard height and genuinely changing insets still deserve investigation. The reproduction demonstrates why the categorical message is too strong. React Native ScrollView contentInset.
A header-presence conditional selects layout spacing. The only caller always supplies the header, including when its tab changes, so
paddingTopstays at 0. The rule nevertheless asserts that changing padding makes rows jump.Reproduction
Reproduced with React Doctor 0.9.13, Node 24.18.0, on macOS. JSON report:
schemaVersion: 3,ok: true, projectcomplete: true. This is a static-analysis reproduction; no runtime jank is inferred from the warning.Create a directory with this
package.json, add the file below, and run:npx --yes react-doctor@0.9.13 . --scope full --json --blocking none --yes --no-score{"name":"react-doctor-rule-repros","private":true,"version":"1.0.0","dependencies":{"react":"19.2.7","react-native":"0.86.2","@shopify/flash-list":"2.3.2","@tanstack/react-query":"5.101.0","zustand":"5.0.14"}}HeaderGrid.tsx:Actual
“Your users see rows jump when a changing paddingTop on contentContainerStyle shifts the whole list.”
The recommendation is
contentInset={{ bottom: dynamicValue }}. That changes the edge from top to bottom and does not preserve this cross-platform content-spacing behavior.Expected
Treat an unresolved expression as a potential cost, not proof that the value changes or causes jank. Either recognize statically provable stable cases or give conditional profiling guidance. The recommendation should preserve the affected edge and explain that contentInset is not a general cross-platform substitute for content padding.
Notes
This is not a request to exempt every prop-based expression: keyboard height and genuinely changing insets still deserve investigation. The reproduction demonstrates why the categorical message is too strong. React Native ScrollView contentInset.